我有一个字符串,我想“unflatten”或“tree-ify”;也就是说,我想从此开始:
F=8|A_C=3|A_B=2|D_G_H=11|D_B=2|E=5
对此:
{ A => { B => 2,C => 3,},D => { B => 2,G => { H => 11,E => 5,F => 8,}
我的策略是分别处理每个管道分隔的字段,并通过=符号分成一个键/值对:
sub unflatten { my ($data) = @_; my @fields = split /\|/,$data; my $result = {}; for my $datum (@fields) { my ($key,$value) = split /=/,$datum; $result->{&processline($key)} = $value; } return $result; }
我在processline函数中尝试了一些递归魔法:
sub processline { my ($key) = @_; my ($first,$rest) = split /_/,$key,2; # split key into at most 2 parts if($rest) { return { $first => &processline($rest) }; # if the key is nested,there will be something in $rest # so recursively process the smaller $rest,and build up the result hashref } else { return $first; } }
不幸的是,这不起作用:
my $header = "F=8|A_C=3|A_B=2|D_G_H=11|D_B=2|E=5"; use Data::Dumper; print Dumper &unflatten($header);
当我这样做时,我得到:
$VAR1 = { 'F' => '8','HASH(0xe9af60)' => '2','HASH(0xe9ae28)' => '11','E' => '5','HASH(0xe9af90)' => '3','HASH(0xe9ae40)' => '2' };
有人可以解释递归解决方案背后的思考过程,还是建议我的Perl出错了?令人沮丧的是,我能够很容易地得出这个功能的反转(扁平化).
解决方法
我相信使用简单的for循环比使用递归更直接.您选择的方法无法工作,因为它仅使用processline返回的单个键来分配值,并且无法创建多级哈希.
unflatten($hash,'key1_key2_key3_key4','value')
如
unflatten($hash->{key1},'key2_key3_key4','value')`
该程序演示了一个普通循环解决方案它使用一个指针$hash,它从结果哈希的根开始,并在列表中的每个键之后向前移动一个级别.
sub unflatten { my $result = {}; for my $item (split /\|/,$_[0]) { my ($keys,$item; my @keys = split /_/,$keys; my $hash = $result; while (@keys > 1) { my $key = shift @keys; $hash->{$key} ||= {}; $hash = $hash->{$key}; } $hash->{$keys[0]} = $value; } return $result; }
产量
$VAR1 = { 'A' => { 'C' => '3','B' => '2' },'F' => '8','D' => { 'G' => { 'H' => '11' },'E' => '5' };
更新
现在我回到了键盘,这是一个递归的解决方案.它导致与原始哈希相同的哈希
use strict; use warnings; use Data::Dumper; my $data = 'F=8|A_C=3|A_B=2|D_G_H=11|D_B=2|E=5'; my $result = {}; unflatten2($result,$_) for split /\|/,$data; print Dumper $result; sub unflatten2 { my ($hash,$data) = @_; if ($data =~ /_/) { my ($key,$data; unflatten2($hash->{$key} ||= {},$rest); } else { my ($key,$val) = split /=/,$data; $hash->{key} = $val; } }
更新
您可能也对Data::Diver
模块感兴趣,该模块适用于此类情况,尽管文档有点笨拙
以下是使用它的解决方案的外观
use strict; use warnings; use Data::Diver qw/ DiveVal /; use Data::Dumper; my $data = 'F=8|A_C=3|A_B=2|D_G_H=11|D_B=2|E=5'; my $result = {}; for (split /\|/,$data) { my ($keys,$val) = split /=/; DiveVal($result,split /_/,$keys) = $val; } print Dumper $result;