Как мне сохранить порядок ключей, которые я добавляю в хеш Perl? - PullRequest
20 голосов
/ 13 октября 2009

Как я могу поддерживать порядок фактического списка после подсчета его появления, используя хэш в следующей программе? Например, <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]";
}

Ответы [ 7 ]

34 голосов
/ 13 октября 2009

Хэши не заказываются, но, как обычно, 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"; 
}
15 голосов
/ 13 октября 2009

Данные в хеш-таблице хранятся в порядке хеш-кода ключей, который в большинстве случаев похож на случайный порядок. Вы также хотите сохранить порядок первого появления каждой клавиши. Вот один из способов решения этой проблемы:

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";
}
10 голосов
/ 13 октября 2009

Из ответа perlfaq4 на «Как заставить мой хэш запомнить порядок, в который я помещал элементы?»


Как я могу заставить мой хеш помнить порядок, в который я помещал элементы?

Используйте Tie :: IxHash из CPAN.

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,...)
6 голосов
/ 13 октября 2009

Просто:

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 голосов
/ 10 марта 2015

Другим вариантом является простой чистый perl Hash::Ordered модуль Дэвида Голдена (@xdg). Вы получаете порядок, но он медленнее, поскольку хеш становится закулисным объектом, и вы используете методы для доступа и изменения элементов хеша.

Вероятно, существуют тесты, которые могут определить, насколько медленнее модуль, чем обычные хэши, но это крутой способ работы со структурами данных ключ / значение в небольших сценариях и достаточно быстрый для меня в такого рода приложениях. В документации упоминается также несколько других подходов к упорядочиванию хеша.

4 голосов
/ 09 марта 2015

Я не уверен, что это всегда лучший метод, но я иногда использовал его. Вместо того, чтобы просто иметь «видимый» тип хэша, он может хранить как счетчик, так и порядок заметок.

По сути, вместо $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";
}
1 голос
/ 29 марта 2019

Хэши - это просто массивы, пока они не назначены в 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};

Возможно, это всего лишь вариант решения «использовать массив в качестве индекса», но я думаю, что оно лучше, потому что вместо того, чтобы вводить свой индекс вручную (или каким-либо другим способом), вы создаете его непосредственно из исходный массив.

...