Сортировка массива хэша несколькими ключами Perl

У меня есть ссылка массива, содержащая хеши (т.е. @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'           
              }
];

Я хочу сортировать его на основе всех трех клавиш (т.е. бренда, поставщика и ПО). Порядок сортировки должен быть брендом первым, затем поставщиком, а затем, наконец, PO.

Реферация массива после сортировки должна быть:

$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'       
              },
];

Ответ 1

Так как <=> и cmp возвращает 0, чтобы указать равенство, а это false, и поскольку логические логические операторы Perl возвращают решающее значение вместо 0 или 1 сортировка по нескольким клавишам так же просто, как наложение нескольких сравнений вместе с or или ||:

@$arr_ref = sort { $a->{brand}    cmp $b->{brand}    or 
                   $a->{supplier} cmp $b->{supplier} or 
                   $a->{PO}       <=> $b->{PO} 
                 } @$arr_ref;

Я предполагаю, что PO - числовое поле, поэтому вместо cmp вы используете <=>.

Ответ 2

Следующее должно отсортировать ссылку на массив и поместить массив обратно в $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}
}

Ответ 3

Вы можете использовать Sort::Key::Multi, распространяемый с помощью Sort:: Key.

В этом случае мы используем ssikeysort, который ожидает блок, который возвращает строку, строку и целое число и сортирует значения по этому кортежу. (s в ssi обозначает строку и i для целого числа.)

use Sort::Key::Multi qw(ssikeysort);

@$arr_ref = ssikeysort { $_->{brand}, $_->{supplier}, $_->{PO} } @$arr_ref;

Вы также можете использовать вариант in-place, который использует меньше памяти:

use Sort::Key::Multi qw(ssikeysort_inplace);

ssikeysort_inplace { $_->{brand}, $_->{supplier}, $_->{PO} } @$arr_ref;