我需要将哈希转换为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 );
Dallaylaen.. 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 };
G. Cito.. 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;
从版本1.39 List::Util::pairs()
返回ARRAY引用作为通过->key
和->value
方法可访问的祝福对象.这个例子使用LEONT的experimental.pm
pragma DDP
并使事情变得更紧凑.
输出:
{ rita [ [0] 18, [1] 19 ], tina [ [0] 4 ], tinku [ [0] 15, [1] 18, [2] 17, [3] 16 ] }
至于哪个更"可读":很难击败简单的"可移动"标准方法,但是使用最新版本的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" ; }
输出:
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工具盒的另一个有用的补充.
Perl6对于从列表中获取键/值对可以更紧凑,因为您可以在for
语句中使用"多个参数",通过元素组遍历列表然后使用push
它们将它们排列成哈希.您可以通过"自动"考虑重叠键的方式执行此操作.比照 这个简短的perl6片段:
my %h ; for-> $k, $v { %h.push($k => $v) ; } %h.perl.say ;
编辑:
友好的人们#perl6
建议一个更简洁的"方法":
my %h.push:.pairup ; %h.perl.say ;
输出:
{:rita(["18", "19"]), :tina("4"), :tinku(["15", "18", "17", "16"])}<>
比照
Miyagawa本人回答了关于Hash::MultiValue
使用的问题.
我提到的另一个答案Hash::MultiValue
.
讨论一些"新的"perl6函数(flip/reverse/invert
)以及它们如何应用于哈希.
一个示例脚本使用Hash::MultiValue
安全地反转散列
不仅仅是perl
编译器的继续开发使得以新的和有趣的方式编写Perl代码成为可能.感谢@miygawa和Paul Evans对Scalar-List-Utils的管理,Hash::MultiValue
即使您的版本perl
与版本5.8一样久,也可以做很酷的事情.您可以尝试在最新版本中提供的功能List::Util
,即使您perl
是很少从这个千年(List::Util
可与perl-5.6
它迎来了21世纪的2000年3月).
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 };
这里的其他响应所涵盖的技术和模式都经过了尝试和真正的习惯用法,这对于充分利用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;
从版本1.39 List::Util::pairs()
返回ARRAY引用作为通过->key
和->value
方法可访问的祝福对象.这个例子使用LEONT的experimental.pm
pragma DDP
并使事情变得更紧凑.
输出:
{ rita [ [0] 18, [1] 19 ], tina [ [0] 4 ], tinku [ [0] 15, [1] 18, [2] 17, [3] 16 ] }
至于哪个更"可读":很难击败简单的"可移动"标准方法,但是使用最新版本的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" ; }
输出:
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工具盒的另一个有用的补充.
Perl6对于从列表中获取键/值对可以更紧凑,因为您可以在for
语句中使用"多个参数",通过元素组遍历列表然后使用push
它们将它们排列成哈希.您可以通过"自动"考虑重叠键的方式执行此操作.比照 这个简短的perl6片段:
my %h ; for-> $k, $v { %h.push($k => $v) ; } %h.perl.say ;
编辑:
友好的人们#perl6
建议一个更简洁的"方法":
my %h.push:.pairup ; %h.perl.say ;
输出:
{:rita(["18", "19"]), :tina("4"), :tinku(["15", "18", "17", "16"])}<>
比照
Miyagawa本人回答了关于Hash::MultiValue
使用的问题.
我提到的另一个答案Hash::MultiValue
.
讨论一些"新的"perl6函数(flip/reverse/invert
)以及它们如何应用于哈希.
一个示例脚本使用Hash::MultiValue
安全地反转散列
不仅仅是perl
编译器的继续开发使得以新的和有趣的方式编写Perl代码成为可能.感谢@miygawa和Paul Evans对Scalar-List-Utils的管理,Hash::MultiValue
即使您的版本perl
与版本5.8一样久,也可以做很酷的事情.您可以尝试在最新版本中提供的功能List::Util
,即使您perl
是很少从这个千年(List::Util
可与perl-5.6
它迎来了21世纪的2000年3月).