Perl:按值排序哈希,然后按键排序

时间:2013-05-23 22:31:42

标签: perl

与此问题类似:sort by subset of perl string

我想首先按值排序,然后按键的子集排序。

我的%哈希

 cat_02 => 0
 cat_04 => 1
 cat_03 => 0
 cat_01 => 3

输出:(可以是按此顺序的键数组)

cat_02 => 0
cat_03 => 0
cat_04 => 1
cat_01 => 3

奖励:关键的二级比较会识别1234_2_C01且小于1234_34_C01(cmp不会)

4 个答案:

答案 0 :(得分:8)

my %hash = (
  cat_02 => 0,
  cat_04 => 1,
  cat_03 => 0,
  cat_01 => 3
);

print "$_ => $hash{$_}\n"
  for sort { $hash{$a} <=> $hash{$b} or $a cmp $b } keys %hash;

排序对值进行数值比较,如果它们相等,则执行or之后的部分,它会对键进行字符串比较。这会给出您要求的输出。

对于包含与非数字内容混合的数字的字符串的智能排序,请从http://www.davekoelle.com/alphanum.html获取字母比较函数,并将$a cmp $b替换为alphanum($a,$b)

答案 1 :(得分:4)

使用Sort :: Key :: modules:

可以轻松完成(快速!)
use Sort::Key::Natural qw( );
use Sort::Key::Maker intnatkeysort => qw( integer natural );

my @sorted_keys = intnatkeysort { $hash{$_}, $_ } keys(%hash);

或者您可以利用数据的属性并使用自然类型:

use Sort::Key::Natural qw( natkeysort );

my @sorted_keys = natkeysort { "$hash{$_}-$_" } keys(%hash);

答案 2 :(得分:3)

如果您有辅助排序首选项,只需在排序例程中添加另一个级别:

my %hash = (
    cat_02 => 0,
    cat_04 => 1,
    cat_03 => 0,
    cat_01 => 3
);

my @sorted = sort { $hash{$a} <=> $hash{$b} || $a cmp $b } keys %hash;
                  #  primary sort method    ^^ secondary sort method
for (@sorted) {
    print "$_\t=> $hash{$_}\n";
}

<强>输出:

cat_02  => 0
cat_03  => 0
cat_04  => 1
cat_01  => 3

答案 3 :(得分:1)

在这种特殊情况下可能不值得,但Schwartzian transform技术也可用于多标准排序。像这样(codepad):

use warnings;
use strict;

my %myhash = (
  cat_2 => 0, cat_04 => 1,
  cat_03 => 0, dog_02 => 3, 
  cat_10 => 0, cat_01 => 3,
);

my @sorted = 
    map { [$_->[0], $myhash{$_->[0]}] } 
    sort { $a->[1] <=> $b->[1]  or  $a->[2] <=> $b->[2] } 
    map { m/([0-9]+)$/ && [$_, $myhash{$_}, $1] } 
    keys %myhash;

print $_->[0] . ' => ' . $_->[1] . "\n" for @sorted;

显然,这种技术的关键是在缓存中使用多个附加元素。

这里有两件事:1)@sorted实际上变成了数组的数组(这个数组的每个元素都是键值对); 2)此示例中的排序基于键的数字后缀(使用数字,而不是字符串比较),但如果需要,可以在任何方向上进行调整。

例如,当密钥匹配模式XXX_DD_XXX(以及应该比较的DD)时,请使用以下内容更改第二个map子句:

    map { m/_([0-9]+)_/ && [$_, $myhash{$_}, $1] }