我需要在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 );
答案 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及以后!
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可以更紧凑地从列表中获取键/值对,因为您可以使用"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。
Hash::MultiValue
usage。Hash::MultiValue
。flip/reverse/invert
) and how they apply to hashes。Hash::MultiValue
to safely invert a hash 不仅仅是perl
编译器的继续开发使得以新的和有趣的方式编写Perl代码成为可能。感谢@miygawa和Paul Evans对Scalar-List-Utils的管理,即使您的Hash::MultiValue
版本与版本5.8一样久,也可以使用perl
执行很酷的操作;你可以尝试List::Util
的最新版本中提供的功能,即使你的perl
几乎不在这个千禧年(List::Util
与perl-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
]
};