Reputation: 449
I have an array reference containing hashes (i.e., @AOH):
$arr_ref = [ { 'brand' => 'A',
'supplier' => 'X',
'PO' => '2'
},
{ 'brand' => 'B',
'supplier' => 'Y',
'PO' => '1'
},
{ 'brand' => 'B',
'supplier' => 'X',
'PO' => '2'
},
{ 'brand' => 'A',
'supplier' => 'X',
'PO' => '1'
},
{ 'brand' => 'B',
'supplier' => 'X',
'PO' => '1'
}
];
I want to sort it on the basis of all three keys (i.e., brand, supplier and PO). The order of sorting should be brand first, then supplier and then finally on PO.
The array reference after sorting should be:
$arr_ref = [ { 'brand' => 'A',
'supplier' => 'X',
'PO' => '1'
},
{ 'brand' => 'A',
'supplier' => 'X',
'PO' => '2'
},
{ 'brand' => 'B',
'supplier' => 'X',
'PO' => '1'
},
{ 'brand' => 'B',
'supplier' => 'X',
'PO' => '2'
},
{ 'brand' => 'B',
'supplier' => 'Y',
'PO' => '1'
},
];
Upvotes: 25
Views: 24845
Reputation: 150573
You can use Sort::Key::Multi, distributed with Sort::Key.
In this case, we're using ssikeysort
, which expects a block that returns a string, a string and an integer, and which sorts the values by that tuple. (The s
in ssi
stands for string and the i
for integer.)
use Sort::Key::Multi qw(ssikeysort);
@$arr_ref = ssikeysort { $_->{brand}, $_->{supplier}, $_->{PO} } @$arr_ref;
You can also use the in-place variant, which uses less memory:
use Sort::Key::Multi qw(ssikeysort_inplace);
ssikeysort_inplace { $_->{brand}, $_->{supplier}, $_->{PO} } @$arr_ref;
Upvotes: 2
Reputation: 62089
Since <=>
and cmp
return 0 to indicate equality, and that's false, and because Perl's logical Boolean operators return the deciding value instead of just 0 or 1, sorting by multiple keys is as easy as stringing multiple comparisons together with or
or ||
:
@$arr_ref = sort { $a->{brand} cmp $b->{brand} or
$a->{supplier} cmp $b->{supplier} or
$a->{PO} <=> $b->{PO}
} @$arr_ref;
I'm assuming that PO is a numeric field, so you use <=>
for it instead of cmp
.
Upvotes: 57
Reputation: 8345
The following should sort the array reference and place the array back into the $arr_ref
:
$arr_ref = [sort by_brand_supplier_PO @$arr_ref];
sub by_brand_supplier_PO {
$a->{brand} cmp $b->{brand} ||
$a->{supplier} cmp $b->{supplier} ||
$a->{PO} <=> $b->{PO}
}
Upvotes: 6