2012-07-16 54 views
3

我需要钥匙的平面列表转换成一个嵌套的哈希值,如下:的Perl:如何将数组嵌套哈希键

我的$哈希= {};

my @array = qw(key1 key2 lastKey Value);

ToNestedHash($ hash,@array);

这样做:

$哈希{ '键1'} { '键2'} { '中的lastKey'} = “值”;

回答

8
sub to_nested_hash { 
    my $ref = \shift; 
    my $h  = $$ref; 
    my $value = pop; 
    $ref  = \$$ref->{ $_ } foreach @_; 
    $$ref  = $value; 
    return $h; 
} 

说明:

  • 在第一个值作为hashref
  • 取最后的值作为值赋给
  • 其余的都是钥匙。
  • 然后创建一个SCALAR引用基准散列。
  • 反复:
    • 取消引用指针,以获得哈希(第一次)或autovivify指针作为哈希
    • 获取关键
    • 哈希位置并分配标参考哈希位置。
    • (下一次将自动生成指定的散列)。
  • 最后,通过参考最内侧的槽来分配值。

我们知道:

  • 一个哈希或数组的乘客只能是标量或引用。
  • 引用是一种标量。 (my $h = {}; my $a = [];)。
  • 因此,\ $ h - > {$ key}是对堆中标量槽的引用,可能是autovivified。
  • 嵌套散列的“级别”可以自动生成散列引用如果我们这样解决它。

这可能是更明确的做到这一点:

foreach my $key (@_) { 
    my $lvl = $$ref = {}; 
    $ref = \$lvl->{ $key }; 
} 

但由于重复使用这些引用的成语,我写这句话完全,因为它是和发布前测试它,而不会出现错误。

至于替代品,以下版本是 “容易”(想出)

sub to_nested_hash { 
    $_[0] //= {}; 
    my $h  = shift; 
    my $value = pop; 
    eval '$h'.(join '', map "->{\$_[$i]}", 0..$#_).' = $value'; 
    return $h; 
} 

但速度较慢约6-7倍。

+0

请问您可以添加解释吗? – simbabque 2012-07-16 13:40:25

+0

+1我认为通过引用它来自动化一个散列元素 – Borodin 2012-07-16 13:51:04

+0

@Borodin,当我发现它会工作时,它是一个*上帝派*。我正在使用一些多级哈希变换来执行一些馈送处理,并加速了我最常用的例程600%-1000%。 – Axeman 2012-07-16 15:25:46

0

Thxs为好东西!

我做到了递归的方式:

sub Hash2Array 
{ 
    my $this = shift; 
    my $hash = shift; 

    my @array; 
    foreach my $k(sort keys %$hash) 
    { 
    my $v = $hash->{$k}; 
    push @array, 
     ref $v eq "HASH" ? $this->Hash2Array($v, @_, $k) : [ @_, $k, $v ]; 
    } 

    return @array; 
} 

这将是有趣的,有所有这些解决方案之间的性能比较...

0

我认为这个代码是更好 - 更适合移动转换为类方法,并根据提供的参数选择性地设置一个值。否则,选定的答案是整齐的。

#!/usr/bin/env perl 

use strict; 
use warnings; 
use YAML; 

my $hash = {}; 

my @array = qw(key1 key2 lastKey); 
my $val = [qw/some arbitrary data/]; 

print Dump to_nested_hash($hash, \@array, $val); 
print Dump to_nested_hash($hash, \@array); 
sub to_nested_hash { 
    my ($hash, $array, $val) = @_; 
    my $ref = \$hash; 
    my @path = @$array; 
    print "ref: $ref\n"; 
    my $h  = $$ref; 
    $ref  = \$$ref->{ $_ } foreach @path; 
    $$ref  = $val if $val; 
    return $h; 
}