2012-07-16 53 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; 
}