Dr.Avalanche Dr.Avalanche - 5 months ago 9
Perl Question

Perl reorder an array of hashes based on content

Have an array of hashes, I want to be able to reorder them, moving the first entry I find that matches a criteria to be the first entry in the array.

using List::Utils first method I can identify what I want to be the first entry in the array. How can I make the found entry the first element in the AoH?

@Borodin

An example of what the data looks like:

CAT1 => 'Foo', CAT2 => 'BAR', TITLE='test1',
CAT1 => 'BAZ', CAT2 => 'BAR', TITLE='test2',
.....


It has many entries. I wish to find the first entry (there could be more than one) where CAT1=BAZ and CAT2=BAR and move it to be the first item in the AoH.

Answer

Without realistic sample data it is hard to help.

You may sort the values of a list according to any criterion that is computable using Perl's sort operator, which takes an expression or a block as its second parameter

The library List::UtilsBy provides operators sort_by etc. that will probably provide a speed advantage if the sort criterion is a complex one



This sets up the data you've given and dumps it using Data::Dump

Then I've used first_index from List::MoreUtils, which finds the index of the first element of the array that conforms to your criteria

$_->{CAT1} eq 'BAZ' and $_->{CAT2} eq 'BAR'

And then an unshift together with a splice removes that element and puts it at the front of the array. There's a check that $i isn't zero to avoid moving an item that's already at the start of the array

Finally another call to dd shows that the matching item has been moved

use strict;
use warnings FATAL => 'all';

use List::MoreUtils 'first_index';
use Data::Dump;

my @data = (
    {
        CAT1  => 'Foo',
        CAT2  => 'BAR',
        TITLE => 'test1',
    },
    {
        CAT1  => 'BAZ',
        CAT2  => 'BAR',
        TITLE => 'test2',
    }
);

dd \@data;

my $i = first_index {
    $_->{CAT1} eq 'BAZ' and $_->{CAT2} eq 'BAR'
} @data;

die if $i < 0;
unshift @data, splice @data, $i, 1 unless $i == 0;

dd \@data;

output

[
  { CAT1 => "Foo", CAT2 => "BAR", TITLE => "test1" },
  { CAT1 => "BAZ", CAT2 => "BAR", TITLE => "test2" },
]
[
  { CAT1 => "BAZ", CAT2 => "BAR", TITLE => "test2" },
  { CAT1 => "Foo", CAT2 => "BAR", TITLE => "test1" },
]
Comments