【问题标题】:Perl: sort a hash by value, then by keyPerl:按值排序哈希,然后按键
【发布时间】:2013-05-23 22:31:42
【问题描述】:

类似于这个问题:Sort by subset of a Perl string

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

我的%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 不会)。

【问题讨论】:

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

标签: perl


【解决方案1】:

当您有第二个排序偏好时,您只需在排序例程中添加另一个级别:

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

【讨论】:

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

用途:

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 获取字母数字比较函数,并将 $a cmp $b 替换为 alphanum($a,$b)

【讨论】:

  • cmp 对字符串和字母中的数字有什么影响,比如说你用 cat 作为键 1234_2_C01_HT1 和 1234_23_C01_HT2 而不是 cat。
  • @Jabda, cmp 按字典顺序比较,即12 cmp 2 将产生-1,而12 &lt;=&gt; 2 将产生1。作为一种特殊情况,如果两个数字中的数字相同,cmp&lt;=&gt; 将产生相同的结果。
【解决方案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);

【讨论】:

    【解决方案4】:

    在这种特殊情况下可能不值得,但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] } 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-01
      • 2018-07-21
      • 1970-01-01
      相关资源
      最近更新 更多