将具有相同键的哈希值转换为Perl中的数组哈希值

时间:2015-02-18 11:59:27

标签: perl data-structures hashtable associative-array perl-data-structures

我需要在perl

中将哈希转换为数组的哈希值

我有:

%hash = (
    tinku => 15,
    tina  => 4,
    rita  => 18,
    tinku => 18,
    tinku => 17,
    tinku => 16,
    rita  => 19
);

我想将其更改为:

%hash =  ( tinku => [ 15, 16, 17, 18 ], rita => [ 18, 19 ], tina => 4 );

5 个答案:

答案 0 :(得分:5)

my %hash = (tinku =>15,tina =>4, rita =>18, 
    tinku =>18, tinku =>17, tinku =>16, rita =>19);

此分配仅保留每个键的最后一个值(即tinku => 16,rita => 19,tina => 4)并忽略之前的值。这是故意这样做的,以允许覆盖哈希分配中的值。例如。

sub some_function {
     my %args = (%sane_defaults, @_);
};

此外,(foo =>(1,2,3))会创建散列(foo => 1,2 => 3),而不是您所期望的。

可能的解决方案可能是:

use strict;
use warnings;
use Data::Dumper;

my @array = (tinku =>15,tina =>4, rita =>18, tinku =>18, 
     tinku =>17, tinku =>16, rita =>19);
my %hash = hash_of_arrays( @array );
print Dumper(\%hash);

sub hash_of_arrays {
     die "Odd number of elements in hash (of arrays) assignment"
          if @_ % 2;
     # I never understood why this is a *warning* :-)

     # populate hash by hand
     my %hash; 
     while (@_) {
          my $key = shift;
          my $value = shift;
          push @{ $hash{$key} }, $value;
          # here hash values automatically become 
          # empty arrayrefs if not defined, thanks Larry
     };
     return %hash; 
     # *tecnically*, this one returns *array* 
     # and converts it back to hash
};

答案 1 :(得分:5)

这里的其他响应所涵盖的技术和模式都经过了尝试和真正的习惯用法,这对于充分利用Perl,理解现有代码以及使用旧的perl编译器的大型安装基础至关重要。只是 为了好玩 我想我提到了其他一些方法:

perl-5.22中有一个相当可读的新语法,可以替代@fugu更经典的方法。对于更加时髦的东西,我会提到@miyagawa's Hash::MultiValue。 Perl 6还有一种很好的方法可以将具有潜在非唯一键的键/值对列表转换为包含具有多个值的键的哈希。

正如其他回应指出的那样,所有这些的“关键”是:

  

对于引用多个值的哈希键,值不仅需要是列表或数组,还需要是匿名数组[ ]或引用。


使用perl-5.22

提供的新语法

Fugu's response显示了标准的Perl习语。使用@names迭代for 0 .. $#names可确保重叠键不会“丢失”,而是指向多个值的匿名数组。使用perl-5.22,我们可以使用pairs()(核心模块)和postfix dereferencing中的List::Util函数将键/值对添加到散列和帐户,以便重叠或重复密钥一种略微不同的方式:

use experimental qw(postderef);
use List::Util qw/pairs/;

my %hash;    
my $a_ref = [ qw/tinku 15 tina 4 rita 18 tinku 18 tinku 17 tinku 16 rita 19/ ];
push $hash{$_->key}->@* , $_->value for pairs @$a_ref;

use DDP;
p %hash;

自版本1.39 List::Util::pairs()返回ARRAY引用,作为可通过->key->value方法访问的祝福对象。该示例使用 LEONT experimental.pm编译指示和DDP来使事情更紧凑。

<强>输出:

{
    rita    [
        [0] 18,
        [1] 19
    ],
    tina    [
        [0] 4
    ],
    tinku   [
        [0] 15,
        [1] 18,
        [2] 17,
        [3] 16
    ]
}

至于哪个更具“可读性”:很难超越简单的“可移动”标准方法,但是使用最新版本的perl5中提供的新语法,我们可以探索新的潜力成语。我真的开始喜欢postfix dereferencing。 TIMTOWTDI及以后!


@ miyagawa的Hash::MultiValue

使用此模块,您可以创建一个Hash::MultiValue对象(有许多方法可以通过各种方式访问​​它)和一个普通的哈希引用,以方便地为每个键使用多个值。

#!/usr/bin/env perl -l
use Hash::MultiValue;
use strict;
use warnings;

my $mvhash = Hash::MultiValue->new(tinku =>15, tina =>4, rita =>18,
                tinku =>18, tinku =>17, tinku =>16, rita =>19);

print "\ntinku's values:\n", join " ", $mvhash->get_all('tinku');

print "\nflattened mvhash:\n", join " ", $mvhash->flatten ;

print "\n ... using mvhash as a hashref:" ;
print join " ", $mvhash->get_all($_) for keys %$mvhash ;

print "\n", '... as a "mixed" hashref with each():';
my $mvhash_ref = $mvhash->mixed ;

while ( my ($k, $v) = each $mvhash_ref ) { 
  print "$k => " , ref $v eq "ARRAY" ? "@{$v}" : "$v" ; 
}

<强>输出:

tinku's values:
15 18 17 16

flattened mvhash:
tinku 15 tina 4 rita 18 tinku 18 tinku 17 tinku 16 rita 19

... using mvhash as a hashref:
15 18 17 16
18 19
4

... as a "mixed" hashref with each():
tinku => 15 18 17 16
rita => 18 19
tina => 4

一旦您的哈希值作为Hash::MultiValue对象可用,您就可以通过各种方式对其进行操作,以快速创建临时副本和哈希引用。只需将它们分配给标量Dump(或使用DDP)以了解其工作原理:

use DDP; 
my $hmulti = $mvhash->multi; p $hmulti ;
my $hmixed = $mvhash->mixed; p $hmixed 

Hash::MultiValue对象使用常规哈希操作有一些限制(dd \$mvhash之类的东西不会显示整个哈希 - 你需要做dd $hash->multi)但是在在某些情况下,以这种方式处理多值哈希是有利的(更具可读性和/或某些功能可能需要更少的代码)。

您仍然需要识别Hash::MultiValue何时/何时有用,因此它并非明确地“更容易”或“更清晰” - 但它是您的perl工具盒的另一个有用的补充。


Perl 6 - 仅供比较

Perl6可以更紧凑地从列表中获取键/值对,因为您可以使用"multiple parameters" in a for statement,按元素组遍历列表,然后使用push将它们排列成哈希。您可以通过“自动”考虑重叠键的方式执行此操作。 cf。这个简短的perl6片段:

my %h ;
for <tinku 15 tina 4 rita 18 tinku 18 tinku 17 tinku 16 rita 19> -> $k, $v { 
    %h.push($k => $v) ;
}
%h.perl.say ;

修改 #perl6上的友好人士提出了一个更简洁的“方法”:

my %h.push: <tinku 15 tina 4 rita 18 tinku 18 tinku 17 tinku 16 rita 19>.pairup ;
%h.perl.say ;

<强>输出:

{:rita(["18", "19"]), :tina("4"), :tinku(["15", "18", "17", "16"])}<>

cf。

不仅仅是perl编译器的继续开发使得以新的和有趣的方式编写Perl代码成为可能。感谢@miygawaPaul EvansScalar-List-Utils的管理,即使您的Hash::MultiValue版本与版本5.8一样久,也可以使用perl执行很酷的操作;你可以尝试List::Util的最新版本中提供的功能,即使你的perl几乎不在这个千禧年(List::Utilperl-5.6合作,它在3月迎来了21世纪2000)。

答案 2 :(得分:3)

你要求不可能!散列只能有唯一的键,因此在您的示例中,您将生成一个散列,该散列将每个唯一名称作为其键,并将每个键的最后一个值作为其值:

#!/usr/bin/perl
use warnings;
use strict; 
use Data::Dumper;

my %hash = (tinku =>15,tina =>4, rita =>18, 
           tinku =>18, tinku =>17, tinku =>16, rita =>19);

print Dumper \%hash;

$VAR1 = {
          'rita' => 19,
          'tina' => 4,
          'tinku' => 16
        };

要制作数组哈希,你可以尝试这样的事情:

my %hash;

my @names = qw(tinku tina rita tinku tinku tinku rita);
my @nums = qw(15 4 18 18 17 16 19);


push @{ $hash{ $names[$_] } }, $nums[$_] for 0 .. $#names;


print Dumper \%hash;

$VAR1 = {
          'rita' => [
                      '18',
                      '19'
                    ],
          'tina' => [
                      '4'
                    ],
          'tinku' => [
                       '15',
                       '18',
                       '17',
                       '16'
                     ]
        };

答案 3 :(得分:2)

您首先不能拥有该哈希值。 Perl 中的哈希必须 具有唯一 密钥

答案 4 :(得分:2)

由于哈希只能包含唯一键,因此请不要将列表分配给哈希,而是使用pairs()中的List::Util进行处理,

use List::Util 'pairs';

my %hash;
push @{ $hash{$_->[0]} }, $_->[1]
 for pairs (tinku =>15,tina =>4, rita =>18, tinku =>18, 
           tinku =>17, tinku =>16, rita =>19);

use Data::Dumper; print Dumper \%hash;

输出

$VAR1 = {
      'tinku' => [
                   15,
                   18,
                   17,
                   16
                 ],
      'rita' => [
                  18,
                  19
                ],
      'tina' => [
                  4
                ]
    };