如何在Perl中从哈希数组创建哈希嵌套的哈希?

4

我有一个哈希数组,所有的键都相同,例如:

my $aoa= [
 {NAME=>'Dave', AGE=>12, SEX=>'M', ID=>123456, NATIONALITY=>'Swedish'},
 {NAME=>'Susan', AGE=>36, SEX=>'F', ID=>543210, NATIONALITY=>'Swedish'},
 {NAME=>'Bart', AGE=>120, SEX=>'M', ID=>987654, NATIONALITY=>'British'},
]

我希望编写一个子例程,使用给定的键层次结构将其转换为哈希值。
my $key_hierarchy_a = ['SEX', 'NATIONALITY'];
aoh_to_hoh ($aoa, $key_hierarchy_a) = @_;
 ...
}

将返回

{M=>
  {Swedish=>{{NAME=>'Dave', AGE=>12, ID=>123456}},
   British=>{{NAME=>'Bart', AGE=>120, ID=>987654}}}, 
 F=>
  {Swedish=>{{NAME=>'Susan', AGE=>36,  ID=>543210}}
}

请注意,这不仅创建了正确的键层次结构,还删除了现在多余的键。
我卡在了需要在正确的层次结构位置创建新的最内部哈希的点上。
问题是我不知道“深度”(即键的数量)。如果我有一个常数,我可以做类似于以下的事情:
%h{$inner_hash{$PRIMARY_KEY}}{$inner_hash{$SECONDARY_KEY}}{...} = filter_copy($inner_hash,[$PRIMARY_KEY,$SECONDARY_KEY])

也许我可以写一个循环,逐级添加一个级别,从哈希表中删除该键,然后将剩余的哈希表添加到“当前”位置,但这有点麻烦,而且我不确定如何在哈希表的哈希中保留“位置”...


2
你期望的数据结构看起来不对。例如,如果有两个瑞典女性,$expected{FEMALE}{Swedish} 应该包含什么?按照你展示的方式(一直使用哈希),这个问题没有一个好的答案。我的假设是 $expected{FEMALE}{Swedish} 需要是一个数组引用,其中包含修剪后的哈希引用。 - FMc
这实际上并不是很难做到,但你必须列出更明确的结构。也许可以在XML中描述层次结构以及哪些是属性/仅出现一次的元素,哪些可以列出多次。 - vol7ron
关于FM所说的,您不需要拥有一个数组引用,但是您需要一些独特的键系统。数组很好,因为它们天生就创建了一个独特的索引。 - vol7ron
2个回答

6
use Data::Dumper;

my $aoa= [
 {NAME=>'Dave', AGE=>12, SEX=>'M', ID=>123456, NATIONALITY=>'Swedish'},
 {NAME=>'Susan', AGE=>36, SEX=>'F', ID=>543210, NATIONALITY=>'Swedish'},
 {NAME=>'Bart', AGE=>120, SEX=>'M', ID=>987654, NATIONALITY=>'British'},
];

sub aoh_to_hoh {
  my ($aoa, $key_hierarchy_a) = @_;
  my $result = {};
  my $last_key = $key_hierarchy_a->[-1];
  foreach my $orig_element (@$aoa) {
    my $cur = $result;
    # song and dance to clone an element
    my %element = %$orig_element;
    foreach my $key (@$key_hierarchy_a) {
      my $value = delete $element{$key};
      if ($key eq $last_key) {
        $cur->{$value} ||= [];
        push @{$cur->{$value}}, \%element;
      } else {
        $cur->{$value} ||= {};
        $cur = $cur->{$value};
      }
    }
  }
  return $result;
}

my $key_hierarchy_a = ['SEX', 'NATIONALITY'];
print Dumper(aoh_to_hoh($aoa, $key_hierarchy_a));

根据@FM的评论,您确实需要在其中添加一个额外的数组级别。
输出结果:
$VAR1 = {
          'F' => {
                   'Swedish' => [
                                  {
                                    'ID' => 543210,
                                    'NAME' => 'Susan',
                                    'AGE' => 36
                                  }
                                ]
                 },
          'M' => {
                   'British' => [
                                  {
                                    'ID' => 987654,
                                    'NAME' => 'Bart',
                                    'AGE' => 120
                                  }
                                ],
                   'Swedish' => [
                                  {
                                    'ID' => 123456,
                                    'NAME' => 'Dave',
                                    'AGE' => 12
                                  }
                                ]
                 }
        };

编辑:哦,顺便说一下 - 如果有人知道如何优雅地克隆引用的内容,请教一下。谢谢!

编辑编辑:@FM 帮了忙。现在一切都好了 :D


Storable::dclone 可以用于通用地复制深层数据结构的内容。 - Ether

2

正如您所经历的那样,编写代码来创建任意深度的哈希结构有点棘手。访问这样的结构的代码同样棘手。这使人想知道: 你真的想这么做吗?

一个更简单的方法可能是将原始信息存储在数据库中。只要您关心的键已经被索引,数据库引擎就能够非常快速地检索到感兴趣的行: 给我所有性别为女性且国籍为瑞典的人。现在听起来很有前途!

您还可以在此问题中找到一些相关的内容。


也许你是对的。我应该有时间了解一下Perl中的数据库。 - David B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接