Сортировка числовых ключей хеша, разделенных дефисом

У меня есть такой хэш

my %hash = (
            '2011-49' => 'data1',
            '2011-100' => 'data2',
            '2009-22' => 'data3',
            '2011-11' => 'data4',
            '4323' => 'data5',
            '2354' => 'data6',
            '423532-2' => 'data7'
           );

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

my %hash = (
            '2009-22' => 'data3',
            '2011-11' => 'data4',
            '2011-49' => 'data1',
            '2011-100' => 'data2',
            '2354' => 'data6',
            '4323' => 'data5',
            '423532-2' => 'data7'
           );

Я нажал все ключи в массиве и отсортировал с помощьюSort::Naturally qw(nsort ncmp) Но это не работает.

2 ответа

Решение

Как я сказал в комментариях, вы не можете сортировать хэш, потому что хэш не сохраняет порядок своих ключей. Вы можете, однако, отсортировать ключи и сохранить их во что-то, что сохраняет порядок, например, массив. Для этой цели мы можем использовать преобразование Шварца:

my @sorted_keys = map $_->[0],                           # 3)
                  sort { $a->[1] <=> $b->[1] ||          
                         $a->[2] <=> $b->[2] }           # 2)
                  map { [ $_, /(\d+)/g ] } keys %hash;   # 1)

С конца мы 1) сначала сохраняем исходную строку, плюс первое и второе число внутри анонимного массива ref. В результате получается список ссылок на массивы - кеш, который мы 2) передаем sortгде они отсортированы по первому номеру, а если они одинаковые - по второму. Это достигается с помощью || внутри sort кодовый блок. Наконец, мы 3) восстановить исходную строку и отменить массив ссылок.

Вы уже получили хороший ответ, но подумали, что я взвеслю Sort:: Versions. Это, кажется, обрабатывает такие операции немного лучше, чем Sort:: Naturally:

use warnings;
use strict;
use Sort::Versions;

my %hash = (
            '2011-49' => 'data1',
            '2011-100' => 'data2',
            '2009-22' => 'data3',
            '2011-11' => 'data4',
            '4323' => 'data5',
            '2354' => 'data6',
            '423532-2' => 'data7'
);

print "$_ => $hash{$_}\n" for ( sort{ versioncmp( $a, $b ) } keys %hash );

Это даст следующий результат:

2009-22 => data3
2011-11 => data4
2011-49 => data1
2011-100 => data2
2354 => data6
4323 => data5
423532-2 => data7

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

Другие вопросы по тегам