我有一个由Hash::Flatten
例如,
flatten( { a => [ 'x', { b => 'y' } ] } )
产生
my $flat = {
'a:0' => 'x'
'a:1.b' => 'y',
};
我想从键列表中生成一个扁平的哈希键,并且键Data::Diver
的函数可以接受索引。
例如,
my @key = ('a', 1, 'b');
应该返回
my $key = "a:1.b";
我看过Hash :: Flatten,但它似乎只能弄平整个哈希,这不是我想要的。我只是想一次压扁一个(嵌套)密钥。
为了避免复制Hash :: Flatten的转义机制,我尝试了以下内容:
use Data::Diver qw( DiveVal );
use Hash::Flatten qw( flatten );
my @key = ('a', 1, 'b');
DiveVal(my $h = {}, @key) = 1;
my ($key) = keys(%{ flatten($h) );
但这可以轻松地将a:0
作为a:1.b
返回。有没有人有任何建议?
答案 0 :(得分:2)
只有您感兴趣的密钥才会有一个定义的值,因此只需要进行一些小的更改。
use Data::Diver qw( DiveVal );
use Hash::Flatten qw( flatten );
sub flat_key {
DiveVal(my $h = {}, @_) = 1;
my $flat = flatten($h);
return ( grep $flat->{$_}, keys(%$flat) )[0];
}
my @key = ('a', 1, 'b');
my $key = flat_key(@key); # a:1.b
因为它使用Data :: Diver,所以您也可以使用引用来指示数字实际上是一个哈希键。
my @key = ('a', 1, 'b');
my $key = flat_key(map \$_, @key); # a.1.b
或者,逃逸机制已有详细记载。
sub _flat_key_escape {
my ($s) = @_;
$s =~ s/([\\.:])/\\$1/g;
return $s;
}
sub flat_key {
my $key;
die("usage") if !@_;
for my $subkey (@_) {
if (ref($subkey)) { $key .= '.' . _flat_key_escape($$subkey); }
elsif ($subkey !~ /^-?[0-9]+\z/) { $key .= '.' . _flat_key_escape($subkey); }
else { $key .= ':' . _flat_key_escape($subkey); }
}
return substr($key, 1);
}
答案 1 :(得分:1)
如果不参考Hash::Flatten
或Data::Diver
,这很容易做到。后者的DiveVal
使用正则表达式/^-?\d+$/
区分散列键和数组索引,因此我们可以做同样的事情来发现序列的Hash::Flatten
默认收缩中的项是否应该以冒号开头:
(数组索引)或点.
(哈希键)。
这使子程序flatten_key
低于
use strict;
use warnings;
use 5.010;
my @key = ('a', 1, 'b');
my $key = flatten_key(@key);
say $key;
say flatten_key(qw/ a b c 1 2 3 /);
sub flatten_key {
join '', shift, map /^-?\d+$/ ? ":$_" : ".$_", @_;
}
<强>输出强>
a:1.b
a.b.c:1:2:3
<强>更新强>
如果你需要使用Data::Diver
约定,任何作为标量引用传递的值都是哈希键,即使它看起来像一个数字,那么你可以扩展那个子程序这个。它稍微有点尴尬,因为序列中的第一项也需要处理,但由于某种原因它不需要分隔符。所以我选择为所有项目添加分隔符,然后从第一个项目中删除它。
say flatten_key('a', 'b', \1, \2, 'c', 'd', 1, 2);
sub flatten_key {
my @key = map {
ref() ? ".$$_" :
/^-?\d+$/ ? ":$_" :
".$_"
} @_;
$key[0] =~ s/^[:.]//;
join '', @key;
}
<强>输出强>
a.b.1.2.c.d:1:2
<强>更新强>
还要考虑本身包含点或冒号的哈希键:
say flatten_key(qw/ a .. :: b /);
sub flatten_key {
my @key = map {
(my $s = ref() ? $$_ : $_) =~ s/(?=[:.\\])/\\/g;
/^-?\d+$/ ? ":$s" : ".$s"
} @_;
$key[0] =~ s/^[:.]//;
join '', @key;
}
<强>输出强>
a.\.\..\:\:.b