Как я могу отсортировать массив Perl из массива хэшей?

@aoaoh;

$aoaoh[0][0]{21} = 31;
$aoaoh[0][0]{22} = 31;
$aoaoh[0][0]{23} = 17;

for $k (0 .. $#aoaoh) {
    for $i(0.. $#aoaoh) {
        for $val (keys %{$aoaoh[$i][$k]}) {
            print "$val=$aoaoh[$i][$k]{$val}\n";
        }
    }
}

Выход:

    22 = 31
    21 = 31
    23 = 17

но я ожидаю, что это будет

    21=31
    22=31
    23=17

Пожалуйста, скажите мне, где это не так.

Также, как мне отсортировать значения так, чтобы я получил вывод как

    23=17 
    22=31
    21=31 (если 2 ключа имеют одинаковое значение, то ключ с более высоким значением идет первым)

4 ответа

Решение

Звучит так, как вы хотите:

for $val (sort keys %{$aoaoh[$i][$k]}) {

а также:

for $val (reverse sort keys %{$aoaoh[$i][$k]}) {

Хотя из вашего комментария похоже, что вы не хотите чисто обратную сортировку. Вы хотите создать свою собственную функцию сортировки:

for $val (sort {$aoaoh[$i][$k]->{$a} <=> $aoaoh[$i][$k]->{$b} || $a <=> $b} keys %{$aoaoh[$i][$k]}) {

Я ответил на два других абсолютно одинаковых вопроса этого пользователя, но похоже, что этот победит, поэтому я отвечаю и здесь.

Из ответа perlfaq4 на Как отсортировать массив по чему-либо?


Предоставьте функцию сравнения для sort() (описана в sort в perlfunc):

@list = sort { $a <=> $b } @list;

Функция сортировки по умолчанию - cmp, сравнение строк, которая сортирует (1, 2, 10) в (1, 10, 2). <=>, использованный выше, является оператором числового сравнения.

Если у вас есть сложная функция, необходимая для извлечения части, по которой вы хотите отсортировать, то не делайте это внутри функции сортировки. Сначала вытащите его, потому что сортировка BLOCK может вызываться много раз для одного и того же элемента. Вот пример того, как вытащить первое слово после первого числа в каждом элементе, а затем отсортировать эти слова без учета регистра.

@idx = ();
for (@data) {
    ($item) = /\d+\s*(\S+)/;
    push @idx, uc($item);
    }
@sorted = @data[ sort { $idx[$a] cmp $idx[$b] } 0 .. $#idx ];

которая также может быть написана таким образом, используя трюк, который стал известен как преобразование Шварца:

@sorted = map  { $_->[0] }
    sort { $a->[1] cmp $b->[1] }
    map  { [ $_, uc( (/\d+\s*(\S+)/)[0]) ] } @data;

Если вам нужно отсортировать по нескольким полям, следующая парадигма будет полезна.

@sorted = sort {
    field1($a) <=> field1($b) ||
    field2($a) cmp field2($b) ||
    field3($a) cmp field3($b)
    } @data;

Это удобно сочетать с предварительным расчетом ключей, как указано выше.

Более подробную информацию об этом подходе вы найдете в статье "Больше, чем вы когда-либо хотели знать" в http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz.

Смотрите также вопрос позже в perlfaq4 о сортировке хэшей.

Ответ на Q1 будет:

print "${$aoaoh[0][0]}{$_}=$_\n" for sort keys %{$aoaoh[0][0]};

что можно написать как:

for (sort keys %{$aoaoh[0][0]}) {
    print "${$aoaoh[0][0]}{$_}=$_\n"
}

И ответ для Q2:

print "$_->[1]=$_->[0]\n" for
map { [$_->[0], $_->[1]] }
sort { $a->[0] cmp $b->[0] }
map { [ ${$aoaoh[0][0]}{$_}, $_ ] } keys %{$aoaoh[0][0]};

Похоже, вы ожидаете, что хэши вернут свои ключи и значения в определенном порядке, где в perl такой гарантии нет.

Вы можете либо реализовать более сложную структуру данных, которая обрабатывает некоторые упорядочения для вас, либо добавить на экран более логику сортировки (вместо просто циклического выполнения), либо загрузить модуль для поддержки упорядоченных хешей, например, Tie::Hash.:: Индексируется.

Я ожидаю, что реализация Tie::Hash::Indexed будет выглядеть так:

my @aoaoh;

use Tie::Hash::Indexed;
tie my %hash, 'Tie::Hash::Indexed';
$aoaoh[0][0] = \%hash;

$aoaoh[0][0]{21} = 31;
$aoaoh[0][0]{22} = 31;
$aoaoh[0][0]{23} = 17;

for $k (0 .. $#aoaoh) {
    for $i(0.. $#aoaoh) {
        for $val (keys %{$aoaoh[$i][$k]}) {
            print "$val=$aoaoh[$i][$k]{$val}\n";
        }
    }
}
Другие вопросы по тегам