如何展平单个嵌套哈希键?

时间:2015-04-15 17:30:48

标签: perl

我有一个由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返回。有没有人有任何建议?

2 个答案:

答案 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::FlattenData::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