【问题标题】:How do I iterate through an array of hashes of arrays once per hash, and not once per nested array element?如何在每个散列中遍历数组的散列数组一次,而不是每个嵌套数组元素一次?
【发布时间】:2012-08-16 06:55:15
【问题描述】:

如果此问题已在其他地方得到解决,我们深表歉意。我找了又找。

我正在使用一个配置文件,该文件通过左对齐每个块的名称和每个名称的缩进参数来分隔数据块(参见下面的__DATA__)。我编写了一个脚本,将每个块存储到一个散列中,其中一个 $hash{name} 键指向一个标量值,一个 %hash{args} 键指向一个值数组。由于每个块都存储在自己的散列中,因此对散列的引用以匿名方式存储在数组中。最终,我想一一获取这些哈希值并处理其中的值,但我在遍历数组时遇到了麻烦。

当我尝试打印存储在每个散列中的值时,它似乎在列表上下文中获取该数组中的散列引用,所以如果 %hash{args} 是对具有三个元素的数组的引用,那就是三倍foreach 循环运行。

我如何让该循环中的代码只为我放入该数组的每个哈希引用运行一次?

如果您检查我的输出,很明显需要取消对嵌套数组引用的引用,但我一直坚持正确地循环,以至于我还无法解决这个问题。也许解决方案可以解决这两个问题?

看:

use strict;
use warnings;

my @array;
my %hash;
my ($name, $args);

while (my $line = <DATA>) {
    chomp($line);        
    if ($line !~ /^\s/) 
    {
        my ($key) = $line =~ /^\S+/g;
        $hash{name} = $key;
        print "Defined a name $key\n";
     } 
     else 
     {
        $line =~ s/^\s+//;
        push (@{ $hash{args} }, $line);
        print "Defined an arg $line\n";
     }
     push (@array, \%hash);
}

foreach my $i (@array)
{
    foreach my $h (keys %{$i}) 
    {
        print $h . "\t";
        print $i->{$h} . "\n";      

    }
}


__DATA__
Sports
    Basketball
    Tennis
    Boxing
Guys
    Tom
    Dick
    Harry

这是输出:

Defined a name Sports
Defined an arg Basketball
Defined an arg Tennis
Defined an arg Boxing
Defined a name Guys
Defined an arg Tom
Defined an arg Dick
Defined an arg Harry
args    ARRAY(0x4a8e24)
name    Guys
args    ARRAY(0x4a8e24)
name    Guys
args    ARRAY(0x4a8e24)
name    Guys
args    ARRAY(0x4a8e24)
name    Guys
args    ARRAY(0x4a8e24)
name    Guys
args    ARRAY(0x4a8e24)
name    Guys
args    ARRAY(0x4a8e24)
name    Guys
args    ARRAY(0x4a8e24)
name    Guys

【问题讨论】:

    标签: arrays perl hash hash-of-hashes


    【解决方案1】:

    我认为您需要不同的数据结构。试试这个:

    use strict;
    use warnings;
    use Data::Dumper;
    
    my @array;
    my %hash;
    my ( $name, $args, $key );
    
    while ( my $line = <DATA> ) {
        chomp($line);
        if ( $line !~ /^\s/ ) {
            $key = $line;  #=~ /^\S+/g;
            print "Defined a name $key\n";
        }
        else {
            $line =~ s/^\s+//;
            push (@{$hash{$key}}, $line);
            print "Defined an arg $line\n";
        }
    }
    
    print Dumper(\%hash);
    
    __DATA__
    Sports
            Basketball
            Tennis
            Boxing
    Guys
            Tom
            Dick
            Harry
    

    输出:

    $VAR1 = {
              'Sports' => [
                            'Basketball',
                            'Tennis',
                            'Boxing'
                          ],
              'Guys' => [
                          'Tom',
                          'Dick',
                          'Harry'
                        ]
            };
    

    我试图让解决方案接近您自己的代码。

    编辑:如果您处理多级数据结构,总是使用Data::Dumper 来转储您的数组/哈希中的确切内容。这将帮助您解决此实例等分配问题。

    您的数据结构如下所示:

    $VAR1 = [
              {
                'args' => [
                            'Basketball',
                            'Tennis',
                            'Boxing',
                            'Tom',
                            'Dick',
                            'Harry'
                          ],
                'name' => 'Guys'
              },
              $VAR1->[0],
              $VAR1->[0],
              $VAR1->[0],
              $VAR1->[0],
              $VAR1->[0],
              $VAR1->[0],
              $VAR1->[0]
            ];
    

    【讨论】:

      【解决方案2】:

      你的问题:

      在构建数据结构时,您犯了一些错误:

      • 您总是将新的$key 分配给相同的$hash{name} 字段。以前的内容被覆盖。最后一个值Guys 保留。
      • 您将%hash 的引用推送到@array。因为引用反映了原始对象的所有变化,所以每次都得到相同的输出。

      解决办法:

      使用不同的数据结构;-)

      您的数据是这样工作的:我们有一个名称集,每个名称都带有一个值列表。在 Perl 中,集合是用哈希建模的,所以我们使用一个:

      my %hash = ();
      

      每个条目将包含对包含值的数组的引用。最终的数据结构将如下所示:

      %hash = (
          Sports => ["Basketball", "Tennis", "Boxing"],
          Guys   => ["Tom", "Dick", "Harry"]
      );
      

      您可以使用Data::Dumper 模块检查您当前的数据结构:

      use Data::Dumper;
      print Dumper (\%name); # has to be given references
      

      要使用此数据结构,只需对您的代码进行最小的改进:

      my %hash;
      my $key;
      while (my $line = <DATA>) {
          chomp $line;        
          if ($line =~ /^(\S+)/) {
              $key = $1;
              $hash{$key} = []; # this is the main difference
              print "Defined a name $key\n";
           } else {
              $line =~ s/^\s+//;
              push @{ $hash{$key} }, $line;
              print "Defined an arg $line\n";
           }
      }
      

      我也稍微改变了编码风格,但本质上唯一的区别是我将名称用作哈希key。我记得最后一个 $key 在循环外使用,以便所有 args 都知道该去哪里。

      当我想打印数据结构时,如果名称的顺序不重要,我会使用each函数:

      while (my ($name, $arrayRef) = each %hash) {
         my @args = @$arrayRef; # dereference the array
         print qq{name $name with args "@args"\n};
      }
      

      while (my ($name, $arrayRef) = each %hash) {
         print "name\t$name\n";
         print "arg\t$_\n" foreach @$arrayRef;
      }
      

      【讨论】:

        猜你喜欢
        • 2023-04-10
        • 2023-03-22
        • 2018-06-29
        • 2012-08-03
        • 2013-04-11
        • 2019-01-31
        • 2016-08-03
        • 2015-07-01
        • 1970-01-01
        相关资源
        最近更新 更多