【问题标题】:Sorting a hash by value when it has many keys当哈希有很多键时按值对哈希进行排序
【发布时间】:2011-10-30 22:14:43
【问题描述】:

我相信这就是您通常按值对哈希进行排序的方式:

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

这将打印出从最小到最大的值。

现在,如果我有这样的哈希怎么办:

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

如何按值排序并同时获取所有键?

【问题讨论】:

  • 所以你想在结构中不考虑深度进行排序?
  • 这个散列中有 3 个键的固定深度,我想按所有存在的键的三元组的值对散列进行排序。

标签: perl sorting hash


【解决方案1】:

我只想创建一个新的哈希:

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";
}

【讨论】:

  • 谢谢,我刚用了这个。
  • 注意$; 必须是一个永远不会出现在您的键中的值(它默认为“\x1c”,一个控制字符),否则此代码将崩溃和燃烧。
【解决方案2】:

我通过将 reference 向下移动到适当的哈希键来完成类似的操作。然后就可以对指针进行排序了。

这样做的好处是在电平变化时很容易调整。

我使用此方法的目的是通过引用键数组系统地将指针移动到特定级别。 (例如:我的@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";
}

【讨论】:

    【解决方案3】:

    出于学术目的,这是一个相当整洁的递归函数:

    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-&gt;[1] &lt;=&gt; $b-&gt;[1] } 或类似名称对其进行排序,然后从@{ $entry-&gt;[0] } 中提取每个条目的键路径。无论数据结构的深度如何,它都可以工作,即使叶节点没有出现在相同的深度。不过,它需要一些扩展来处理不是纯粹的 hashrefs 和普通标量的结构。

    【讨论】:

      【解决方案4】:

      这是一种使用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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-20
        • 2011-10-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-10
        相关资源
        最近更新 更多