【问题标题】:How can I maintain the order of keys I add to a Perl hash?如何维护添加到 Perl 哈希的键的顺序?
【发布时间】:2010-12-06 05:29:55
【问题描述】:

在以下程序中使用哈希计算实际列表的出现后,如何维护实际列表的顺序?例如,<DATA>

a
b
e
a
c 
d 
a
c
d
b
etc.

使用哈希,我计算了每个元素的出现次数。

我想要的是:

a  3
b  2
e  1
c  2
d  2

但下面的程序向我展示了其他方式。

my (%count, $line, @array_1, @array_2);
while ($line = <DATA>) {
    $count{$line}++ if ( $line =~ /\S/ );
}
@array_1 = keys(%count);
@array_2 = values(%count);
for(my $i=0; $i<$#array_1; $i++)
{
   print "$array_1[$i]\t $array_2[$i]";
}

【问题讨论】:

  • 能否请您标记您的代码,以便阅读。
  • @Sinan:不……不是那样……我找不到承认所有精彩答案的地方……只是想知道是通过添加评论完成还是还有其他地方...... stackoverflow 和 perl 的新手......对不起,也许我应该花一些时间来学习如何积极参与这个平台......几乎所有的答案都给了我一个新的想法学习...并服务于我的目的...
  • @Cthar - 您应该选择最适合您的问题的答案(同时考虑到它的写得好和易于理解)并通过单击问题旁边的勾号来接受它。您可以通过投票来赞扬其他好的答案。当然也欢迎积极的 cmets。做这些事情可以帮助其他人找到最好的答案,也可以奖励那些帮助你的人。
  • 我为维护订单所做的就是在我的物品前面加上一个数字......例如,如果我有 1000 件物品,第一个可能是 '0001 applesauce',然后是 '0002 aardvarkberries'然后如果我使用实际的条目名称,我知道我必须首先删除 '^\d{4}\s(.+)' 并使用 $1 (假设苹果酱和 aardvarkberries 是我首选的两个哈希键)。我从 Cisco 如何维护其 EEM 脚本顺序中获得了这个想法,并且它对我的目的非常有效。
  • 实际上,上述发帖人的想法听起来像是我使用过的另一种技术——我为该哈希条目的顺序保留了一个分配。但是,然后,我需要反复遍历哈希以填充一个有序列表,然后我引用该键本身。维护键名列表还有其他用途,例如当您读取 JSON 式输入文件并希望填充与输入文件相同深度的哈希时(您将拥有动态深度的推送'ed/pop'ed 深度基于指标,比如散列中的括号,告诉你什么时候执行哪个操作)。

标签: perl data-structures hash perl-data-structures


【解决方案1】:

哈希没有排序,但像往常一样,CPAN 提供了一个解决方案:Tie::IxHash

use Tie::IxHash;
my %count;
tie %count, 'Tie::IxHash';

while ($line = <DATA>) {
$count{$line}++ if ( $line =~ /\S/ );
}

while( my( $key, $value)= each %count) {
    print "$key\t $value"; 
}

【讨论】:

    【解决方案2】:

    哈希表中的数据是按照键的哈希码的顺序存储的,在大多数情况下,这就像一个随机顺序。您还想存储每个键第一次出现的顺序。这是解决此问题的一种方法:

    my (%count, $line, @display_order);
    while ($line = <DATA>) {
        chomp $line;           # strip the \n off the end of $line
        if ($line =~ /\S/) {
            if ($count{$line}++ == 0) {
                # this is the first time we have seen the key "$line"
                push @display_order, $line;
            }
        }
    }
    
    # now @display_order holds the keys of %count, in the order of first appearance
    foreach my $key (@display_order)
    {
        print "$key\t $count{$key}\n";
    }
    

    【讨论】:

    • 恕我直言,这是一个比使用 Tie::IxHash 更好的解决方案,我认为这超出了 OP 的原始需求。更适合使用按键的显示顺序,如this answer,或者使用foreach my $key (sort keys %count) { ... }
    【解决方案3】:

    来自perlfaq4"How can I make my hash remember the order I put elements into it?"的回复


    如何让我的哈希记住我将元素放入其中的顺序?

    使用 CPAN 中的 Tie::IxHash。

    use Tie::IxHash;
    
    tie my %myhash, 'Tie::IxHash';
    
    for (my $i=0; $i<20; $i++) {
        $myhash{$i} = 2*$i;
        }
    
    my @keys = keys %myhash;
    # @keys = (0,1,2,3,...)
    

    【讨论】:

      【解决方案4】:

      简单地说:

      my (%count, @order);
      while(<DATA>) {
        chomp;
        push @order, $_ unless $count{$_}++;
      }
      print "$_ $count{$_}\n" for @order;
      __DATA__
      a
      b
      e
      a
      c
      d
      a
      c
      d
      b
      

      或作为单线器

      perl -nlE'$c{$_}++or$o[@o]=$_}{say"$_ $c{$_}"for@o'<<<$'a\nb\ne\na\nc\nd\na\nc\nd\nb'
      

      【讨论】:

        【解决方案5】:

        另一个选项是 David Golden (@xdg) 的简单纯 perl Hash::Ordered 模块。您获得了订单,但速度较慢,因为散列成为幕后的对象,并且您使用访问和修改散列元素的方法。

        可能有一些基准可以量化模块比常规哈希慢多少,但这是在小脚本中处理键/值数据结构的一种很酷的方法,并且在那种应用程序中对我来说足够快。该文档还提到了其他几种排序哈希的方法。

        【讨论】:

          【解决方案6】:

          我不相信这总是一种更好的技术,但我有时会使用它。它不仅可以存储“可见”类型的哈希,还可以存储注意到的计数和顺序。

          基本上,$count{$line} 不是看到的次数,$count{$line}{count} 是看到的次数,$count{$line}{order} 是看到的顺序。

          my %count;
          while (my $line = <DATA>) {
              chomp $line;
              if ($line =~ /\S/) {
                  $count{$line} ||= { order => scalar(keys(%count)) };
                  $count{$line}{count}++;
              }
          }
          
          for my $line (sort { $count{$a}{order} <=> $count{$b}{order} } keys %count ) {
              print "$line $count{$line}{count}\n";
          }
          

          【讨论】:

            【解决方案7】:

            在 Perl 中分配之前,哈希只是数组,因此如果将其转换为数组,则可以按原始顺序对其进行迭代:

            my @array = ( z => 6,
                          a => 8,
                          b => 4 );
            
            for (my $i=0; $ar[$i]; ++$i) {
                next if $i % 2;
                my $key = $ar[$i];
                my $val = $ar[$i+1];
            
                say "$key: $val"; # in original order
            }
            

            如果你明显地这样做,你就会失去哈希索引的好处。但是由于散列只是一个数组,您可以通过将数组分配给散列来创建一个:

            my %hash = @array;
            say $hash{z};
            

            这可能只是“使用数组作为索引”解决方案的一种变体,但我认为它更简洁,因为您无需手动(或以其他方式)输入索引,而是直接从源数组。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2023-04-07
              • 2012-06-27
              • 1970-01-01
              • 2022-01-19
              • 1970-01-01
              • 1970-01-01
              • 2020-12-08
              • 2020-12-12
              相关资源
              最近更新 更多