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

idi*_*erl 1 perl associative-array hashtable data-structures perl-data-structures

我需要将哈希转换为perl中的数组哈希

我有:

%hash = (
    tinku => 15,
    tina  => 4,
    rita  => 18,
    tinku => 18,
    tinku => 17,
    tinku => 16,
    rita  => 19
);
Run Code Online (Sandbox Code Playgroud)

我想将其更改为:

%hash =  ( tinku => [ 15, 16, 17, 18 ], rita => [ 18, 19 ], tina => 4 );
Run Code Online (Sandbox Code Playgroud)

Dal*_*aen 5

my %hash = (tinku =>15,tina =>4, rita =>18, 
    tinku =>18, tinku =>17, tinku =>16, rita =>19);
Run Code Online (Sandbox Code Playgroud)

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

sub some_function {
     my %args = (%sane_defaults, @_);
};
Run Code Online (Sandbox Code Playgroud)

此外,(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
};
Run Code Online (Sandbox Code Playgroud)


G. *_*ito 5

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

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

正如其他回应指出的那样,所有这一切的"关键"是:

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


使用新的语法 perl-5.22

Fugu的回应显示了标准的Perl习语.通过@names使用进行迭代for 0 .. $#names 可确保重叠键不会"丢失",而是指向多个值的匿名数组.与perl-5.22我们可以使用pairs()从函数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;
Run Code Online (Sandbox Code Playgroud)

从版本1.39 List::Util::pairs()返回ARRAY引用作为通过->key->value方法可访问的祝福对象.这个例子使用LEONTexperimental.pmpragma DDP并使事情变得更紧凑.

输出:

{
    rita    [
        [0] 18,
        [1] 19
    ],
    tina    [
        [0] 4
    ],
    tinku   [
        [0] 15,
        [1] 18,
        [2] 17,
        [3] 16
    ]
}
Run Code Online (Sandbox Code Playgroud)

至于哪个更"可读":很难击败简单的"可移动"标准方法,但是使用最新版本的perl5中提供的新语法,我们可以探索新习语的潜力.我真的开始喜欢postfix dereferencing.TIMTOWTDI及其他!


@宫川的 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" ; 
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

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

use DDP; 
my $hmulti = $mvhash->multi; p $hmulti ;
my $hmixed = $mvhash->mixed; p $hmixed 
Run Code Online (Sandbox Code Playgroud)

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

你仍然需要识别何时/何时Hash::MultiValue有用,因此它并不是毫不含糊地"更容易"或"更清洁" - 但它是你的perl工具盒的另一个有用的补充.


Perl 6 - 仅供比较

Perl6对于从列表中获取键/值对可以更紧凑,因为您可以for语句中使用"多个参数",通过元素组遍历列表然后使用push它们将它们排列成哈希.您可以通过"自动"考虑重叠键的方式执行此操作.比照 这个简短的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 ;
Run Code Online (Sandbox Code Playgroud)

编辑: 友好的人们#perl6建议一个更简洁的"方法":

my %h.push: <tinku 15 tina 4 rita 18 tinku 18 tinku 17 tinku 16 rita 19>.pairup ;
%h.perl.say ;
Run Code Online (Sandbox Code Playgroud)

输出:

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

比照

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