【问题标题】:Perl looping through hash of arrays for single elementsPerl循环遍历单个元素的数组哈希
【发布时间】:2012-07-27 04:18:18
【问题描述】:

我有一个数组哈希,如图:

my %hash = (
  234 => ["aa", "bb", "aa", "ab", "aa"],
  235 => ["aa", "ab", "aa", "bb", "aa"],
  236 => ["bb", "aa", "aa", "aa", "bb"],
  ...
)

我正在尝试遍历每个值的第一个元素并计算bb 的出现次数。

例如,在上面的哈希中,我有一个 bb 出现在显示的所有第一个元素中。我需要将该数字(所有数组的第 0 个元素中“bb”的数量)推入一个新数组,然后移动到下一组元素。

对于上面的示例,我将遍历数组的所有 4 个元素并获得带有 (1, 1, 0, 1, 1) 的最终数组。

我在正确编写代码时遇到了很多麻烦,而且还没有找到类似的问题。任何帮助将非常感激。

【问题讨论】:

  • 如果您使用实际的 perl 语法展示了数据结构的样子,会更容易理解您的问题。
  • 记下下次,感谢Borodin的编辑!

标签: arrays perl loops hash


【解决方案1】:

一个简单的解决方案:迭代哈希值。对于每个值,遍历数组的成员并在需要时将 1 添加到结果数组:

#!/usr/bin/perl
use warnings;
use strict;

my %hash = (234 => [qw/aa bb aa ab aa/],
            235 => [qw/aa ab aa bb aa/],
            236 => [qw/bb aa aa aa bb/],
           );

my @result;

for my $value (values %hash) {
    my $i = 0;
    for (@$value) {
        $result[$i++] += 'bb' eq $_;
    }
}

print "@result\n";

【讨论】:

  • 哦,所以那就是它的意思。好答案;我想知道你为什么不只是循环 values %hash,因为除了获得价值之外,你没有将 $key 用于任何事情。
  • @choroba:我刚刚学到了一些关于 Perl 的知识。我没想到$result[$i++] += 'bb' eq $_ 会在没有运行时错误的情况下逃脱,但在perlop 中发现了这一点。 关系运算符...返回 1 表示 true 和定义的空字符串的特殊版本 "",它算作零,但不会收到有关不正确数字转换的警告
【解决方案2】:
use Data::Dumper;

%hash = (234 => [aa, bb, aa, ab, aa],
         235 => [aa, ab, aa, bb, aa],
         236 => [bb, aa, aa, aa, bb],
        );

do { $i = 0; $element{$i++}->{$_}++ for @{$_}; } for (values %hash);

print Dumper(\%element);

【讨论】:

    【解决方案3】:

    这是一种更加 perl-ish(不一定更好)的做事方式。除非那些维护代码的人非常了解 perl,否则它会不太清楚。尽管如此,学习 map 和 grep 并不是一个坏主意。一旦你熟悉了它们,它们可以帮助你的代码的意图更加清晰。我建议查看每个解决方案并尝试找出每个解决方案的工作原理。

    此解决方案将处理太短的数组,尽管这可能是也可能不是您正在寻找的内容,具体取决于应用程序。

    use warnings;
    use strict;
    
    use Data::Dumper;
    
    my %hash = (
        '234' => [qw(aa bb aa ab aa)],
        '235' => [qw(aa ab aa bb aa)],
        '236' => [qw(bb aa aa aa bb)],
    );
    
    my @result = ();
    
    while ( grep {scalar @{$_}} values %hash ) {
        push @result, scalar @{[
            grep {$_ eq 'bb'}
                map {shift @{$_}}
                    values %hash
        ]};
    }
    
    print Dumper \@result;
    

    【讨论】:

      【解决方案4】:

      grep operator 是计算列表中符合特定条件的值的最佳工具。只需遍历散列值列表的每个条目即可获得每个元素的计数。

      此代码假定每个哈希值数组具有相同的长度,并使用第一个元素的长度作为所有它们的大小。

      use strict;
      use warnings;
      
      my %data = (
        234 => [ qw/aa bb aa ab aa/ ],
        235 => [ qw/aa ab aa bb aa/ ],
        236 => [ qw/bb aa aa aa bb/ ],
      );
      
      my @count_bb;
      for my $i ( 0 .. $#{(values %data)[0]} ) {
        $count_bb[$i] = grep { $_->[$i] eq 'bb' } values %data;
      } 
      
      print "@count_bb\n";
      

      输出

      1 1 0 1 1
      

      此替代方法将创建一个散列,其中包含散列值数组中每个不同值的计数。第一步初始化散列以在每个元素中具有正确数量的零,第二步增加数据中遇到的每个值的计数,最后循环打印生成的 %counts 散列的内容。

      use strict;
      use warnings;
      
      my %data = (
        234 => [ qw/aa bb aa ab aa/ ],
        235 => [ qw/aa ab aa bb aa/ ],
        236 => [ qw/bb aa aa aa bb/ ],
      );
      
      my %counts;
      $counts{$_} = [ (0) x @{(values %data)[0]} ] for map @$_, values %data;
      
      for my $i ( 0 .. $#{(values %data)[0]} ) {
        $counts{$_}[$i]++ for map $_->[$i], values %data;
      } 
      
      while (my ($k, $v) = each %counts) {
          printf "%s => (%s)\n", $k, join ', ', @$v;
      }
      

      输出

      ab => (0, 1, 0, 1, 0)
      bb => (1, 1, 0, 1, 1)
      aa => (2, 1, 3, 1, 2)
      

      【讨论】:

      • 谢谢,第一种方法对我很有效。非常感谢您的帮助。
      猜你喜欢
      • 1970-01-01
      • 2016-08-20
      • 2013-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-27
      • 2012-08-22
      • 1970-01-01
      相关资源
      最近更新 更多