如何维护添加到Perl散列的键的顺序?

8zzbczxx  于 2023-10-24  发布在  Perl
关注(0)|答案(7)|浏览(138)

在下面的程序中,我如何在使用散列计数其出现次数后保持实际列表的顺序?例如,<DATA>

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

使用hash,我计算了每个元素的出现次数。
我想要的是:

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

soat7uwm1#

哈希没有排序,但像往常一样,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"; 
}
2ekbmq32

2ekbmq322#

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

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

qlvxas9a3#

perlfaq4对“如何让我的哈希记住我把元素放进去的顺序”的回答中可以看出。
我如何让我的哈希记住我把元素放进去的顺序?
使用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,...)
mutmk8jj

mutmk8jj4#

简单地说:

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'
8qgya5xd

8qgya5xd5#

另一个选择是大卫戈尔登(@xdg)的简单的纯perl Hash::Ordered模块。您获得了顺序,但它较慢,因为散列成为幕后的对象,您使用方法访问和修改散列元素。
可能有一些基准测试可以量化这个模块比常规哈希慢多少,但是在小脚本中使用键/值数据结构是一种很酷的方式,并且对我来说在这种应用程序中足够快。文档还提到了其他几种排序哈希的方法。

ars1skjm

ars1skjm6#

我并不认为这是一个更好的技术,但我有时会使用它,它可以存储注意到的计数和顺序,而不仅仅是“看到”类型的哈希。
基本上,不是$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";
}
cpjpxq1n

cpjpxq1n7#

哈希在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};

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

相关问题