【问题标题】:How do I consolidate a hash in Perl?如何在 Perl 中合并哈希?
【发布时间】:2011-08-03 02:37:15
【问题描述】:

我有一个哈希引用数组。哈希包含 2 个键,USER 和 PAGES。这里的目标是遍历哈希引用数组,并保持用户在打印机上打印的页面总数(这来自事件日志)。我从 Excel 电子表格中提取数据并使用正则表达式提取用户名和页面。电子表格中有 182 行,每行包含一个用户名和他们在该作业上打印的页数。目前该脚本可以使用用户名和他们打印的页面打印每个打印作业(全部 182 个),但我想将其合并,以便它显示:用户名 266(即只显示一次用户名,以及他们打印的总页数整个电子表格。

这是我尝试遍历哈希引用数组,查看用户是否已经存在,如果存在,+= 该用户的页数进入新的哈希引用数组(较小的)。如果没有,则将用户添加到新的哈希引用数组中:

my $criteria = "USER";
my @sorted_users = sort { $a->{$criteria} cmp $b->{$criteria} } @user_array_of_hash_refs;

my @hash_ref_arr;
my $hash_ref = \@hash_ref_arr;

foreach my $index (@sorted_users)
{
    my %hash = (USER=>"",PAGES=>"");
    if(exists $index{$index->{USER}})
    {
        $hash{PAGES}+=$index->{PAGES};
    }
    else
    {
        $hash{USER}=$index->{USER};
        $hash{PAGES}=$index->{PAGES};
    }
    push(@hash_ref_arr,{%hash});
}

但它给了我一个错误:

全局符号“%index”需要显式的包名...

也许我的逻辑不是最好的。我应该改用数组吗?考虑到我的数据的性质,似乎散列是最好的选择。我只是不知道如何将哈希引用数组缩小到只得到一个用户名和他们打印的总页数(我知道我看起来很多余,但我只是想清楚)。谢谢。

【问题讨论】:

    标签: arrays perl hash reference


    【解决方案1】:
    my %totals;
    
    $totals{$_->{USER}} += $_->{PAGES} for @user_array_of_hash_refs;
    

    然后,取出数据:

    print "$_ : $totals{$_}\n" for keys %totals;
    

    您也可以按使用情况排序:

    print "$_ : $totals{$_}\n" for sort { $totals{$a} <=> $totals{$b} } keys %totals;
    

    【讨论】:

      【解决方案2】:

      正如 mkb 提到的,错误在以下行:

      if(exists $index{$index->{USER}})
      

      但是,在阅读您的代码后,您的逻辑是错误的。简单地纠正语法错误不会提供您想要的结果。

      我建议在循环中跳过使用临时哈希。只需直接使用结果哈希即可。

      例如:

      #!/usr/bin/perl
      use strict;
      use warnings;
      
      my @test_data = (
          { USER => "tom", PAGES => "5" },
          { USER => "mary", PAGES => "2" },
          { USER => "jane", PAGES => "3" },
          { USER => "tom", PAGES => "3" }
      );
      
      my $criteria = "USER";
      my @sorted_users = sort { $a->{$criteria} cmp $b->{$criteria} } @test_data;
      
      my %totals;
      
      for my $index (@sorted_users) {
          if (not exists $totals{$index->{USER}}) {
              # initialize total for this user
              $totals{$index->{USER}} = 0;
          }
      
          # add to user's running total
          $totals{$index->{USER}} += $index->{PAGES}
      }
      
      print "$_: $totals{$_}\n" for keys %totals;
      

      这会产生以下输出:

      $ ./test.pl
      jane: 3
      tom: 8
      mary: 2
      

      【讨论】:

      • 美丽。正是我需要的。太感谢了。大家真的。都非常有帮助。我真的很感激。
      • 循环内不需要if 语句。如果没有,Perl 会自动创建一个条目。此外,打印语句看起来更好:print Dumper \%totals;
      • 我不明白你为什么对输入进行排序。我的意思是,当然,您已经从原始问题中复制了它,但在那里似乎也毫无意义:-)
      • 感谢您的意见。我不知道传递哈希引用会像这样改变 Dumper 输出。关于sort,我只是使用问题中的代码作为我的起点。
      • 编辑了代码以使用 daveorg 的 print 语句(我真的很喜欢它的简洁性)。我保留 if 语句主要是为了说明所涉及的逻辑(作为与问题代码中尝试的内容的比较)。
      【解决方案3】:

      错误来自这一行:

      if(exists $index{$index->{USER}})
      

      Perl 5 中的$ 符号在名称后带有{} 表示您从哈希中获取标量值。名称 %index 没有声明散列。我认为您可能只需要添加一个-&gt; 运算符,因此问题行变为:

      if(exists $index->{$index->{USER}})
      

      但没有数据让我不确定。

      另外,使用 use strict 对你有好处,否则你会默默地实例化 %index 哈希并想知道为什么你的结果没有任何意义。

      【讨论】:

      • 您的添加确实修复了我的错误,但输出与预期不符。它显示所有打印作业(即每次用户出现在文件中以及与之相关的页面时)。所以我的逻辑有问题。
      【解决方案4】:
      my %total;
      for my $name_pages_pair (@sorted_users) {
        $total{$name_pages_pair->{USER}} += $name_pages_pair->{PAGES};
      }
      
      for my $username (sort keys %total) {
        printf "%20s %6u\n", $username, $total{$username};
      }
      

      【讨论】:

      • 我收到一堆错误行:在 win32excel.pl 第 85 行使用未初始化的值 (+)。但它确实只打印了每个用户名一次,但总共有 0 页。跨度>
      • 我使用了你的打印循环,所以我希望我能将你们的答案归功于你们。非常感谢!
      猜你喜欢
      • 2014-04-16
      • 1970-01-01
      • 2010-09-25
      • 2012-04-28
      • 1970-01-01
      • 1970-01-01
      • 2023-04-07
      • 2011-11-22
      • 2013-04-18
      相关资源
      最近更新 更多