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

3

类似于这个问题:使用 Perl 字符串的子集进行排序

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

我的%hash

 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不会这样做)。


1
2_在数值和字符串上都比34小,所以这不是一个好的例子。但我知道你的意思。我更新了我的答案。 - user2404501
4个回答

10

使用:

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后面的部分,该部分对键进行字符串比较。 这会输出您要求的结果。
为了智能地排序包含数与非数混合的字符串,请从The Alphanum Algorithm获取alphanum比较函数,并用alphanum($a,$b)替换$a cmp $b

cmp对于字符串中的数字和字母有什么影响?比如说,如果你的键值不是“cat”,而是“1234_2_C01_HT1”和“1234_23_C01_HT2”,会有什么不同? - Jabda
1
@Jabda,cmp按字典顺序进行比较,即12 cmp 2将产生-1,而12 <=> 2将产生1。作为一个特殊情况,如果两个数字的位数相同,则cmp<=>将产生相同的结果。 - n0rd

4
当您有第二排序偏好时,只需在排序例程内添加另一个级别即可:
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

1
没有所谓的“级别”、“主排序方法”或“次排序方法”,只是布尔表达式,首先比较值,如果相等则比较键。我的意思是,目前的解释看起来像是“这是一些按x排序然后按y排序的魔法语法”。 - n0rd
当然有排序方法,其中一个是主要的,另一个是次要的,从优先级的角度来看。它并不神奇,正如在代码中明显可见的那样,它只是使用逻辑 OR 运算符。 - TLP

3
使用Sort::Key::模块可以轻松(快速!)完成此操作:
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);

1

在这种特定情况下可能并不值得,但是施瓦兹变换技术也可以用于多标准排序。像这样(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] } 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接