Сортировка хеша по значению, когда у него много ключей - PullRequest
5 голосов
/ 20 августа 2011

Я полагаю, что именно так вы обычно сортируете хеш по значению:

foreach my $key (sort { $hash{$a} <=> $hash{$b} } (keys %hash) ) {
    print "$key=>$hash{$key}";
}

Это выведет значения от наименьшего к наибольшему.

А что если у меня есть такой хеш-код:

$hash{$somekey}{$somekey2}{$thirdkey}

Как можно отсортировать по значениям и получить все ключи?

Ответы [ 4 ]

3 голосов
/ 20 августа 2011

Я бы просто создал новый хеш:

my %new;
for my $k1 (keys %hash) {
  for my $k2 (keys %{$hash{$k1}}) {
    for my $k3 (keys %{$hash{$k1}{$k2}}) {
      $new{$k1,$k2,$k3} = $hash{$k1}{$k2}{$k3};
    }
  }
}

my @ordered = sort { $new{$a} <=> $new{$b} } keys %new;
for my $k (@ordered) {
  my @keys = split($;, $k);
  print "key: @k      - value: $new{$k}\n";
}
1 голос
/ 20 августа 2011

Для академических целей приведем довольно аккуратную рекурсивную функцию:

sub flatten_hash {
  my ($hash, $path) = @_;
  $path = [] unless defined $path;

  my @ret;

  while (my ($key, $value) = each %$hash) {
    if (ref $value eq 'HASH') {
      push @ret, flatten_hash($value, [ @$path, $key ]);
    } else {
      push @ret, [ [ @$path, $key ], $value ];
    }
  }

  return @ret;
}

, которая принимает хеш вроде

{
    roman => {
        i => 1,
        ii => 2,
        iii => 3,
    },
    english => {
        one => 1,
        two => 2,
        three => 3,
    },
}

и превращает его в список типа

(
    [ ['roman','i'], 1 ],
    [ ['roman', 'ii'], 2 ],
    [ ['roman', 'iii'], 3 ],
    [ ['english', 'one'], 1 ],
    [ ['english', 'two'], 2 ],
    [ ['english', 'three'], 3 ]
)

хотя, конечно, порядок должен меняться.Учитывая этот список, вы можете отсортировать его по { $a->[1] <=> $b->[1] } или аналогичному, а затем извлечь путь ключа из @{ $entry->[0] } для каждой записи.Он работает независимо от глубины структуры данных и даже если конечные узлы не встречаются на одной и той же глубине.Однако требуется немного расширения для работы со структурами, которые не являются чисто хеш-рефами и простыми скалярами.

1 голос
/ 20 августа 2011

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

Преимущество такого способа состоит в том, что его легко настроить при изменении уровня.

То, для чего я использовал эту методологию, - это систематическое перемещение указателя на определенный уровень путем ссылки на массив ключей. (Например: my @Keys = ('Value', 'Value2');)

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

my $list_ref;
my $pointer;

my %list = (
   Value => {
      Value2 => {
         A => '1',
         C => '3',
         B => '2',
      },
   },
);

$list_ref = \%list;
$pointer = $list_ref->{Value}->{Value2};

foreach my $key (sort { $pointer->{$a} <=> $pointer->{$b} } (keys %{$pointer})) {
   print "Key: $key\n";
}
1 голос
/ 20 августа 2011

Вот способ сделать это, используя Deep :: Hash :: Utils .

use Deep::Hash::Utils qw(slurp);

my %h = (
    A => {
        Aa => { Aaa => 4, Aab => 5 },
        Ab => { Aba => 1 },
        Ac => { Aca => 2, Acb => 9, Acc => 0 },
    },
    B => {
        Ba => { Baa => 44, Bab => -55 },
        Bc => { Bca => 22, Bcb => 99, Bcc => 100 },
    },
);

my @all_keys_and_vals = slurp \%h;
print "@$_\n" for sort { $a->[-1] <=> $b->[-1] } @all_keys_and_vals;

Выход:

B Ba Bab -55
A Ac Acc 0
A Ab Aba 1
A Ac Aca 2
A Aa Aaa 4
A Aa Aab 5
A Ac Acb 9
B Bc Bca 22
B Ba Baa 44
B Bc Bcb 99
B Bc Bcc 100
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...