How can I create a hash hash from an array of hashes in Perl?

I have an array of hashes, all with the same set of keys, for example:

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'}, ] 

I would like to write a routine that converts this to a hash of hashes using a given key hierarchy:

 my $key_hierarchy_a = ['SEX', 'NATIONALITY']; aoh_to_hoh ($aoa, $key_hierarchy_a) = @_; ... } 

will return

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

Please note that this not only creates the correct key hierarchy, but also removes the extra keys now.

I am stuck at the point where I need to create a new, innermost hash in the correct hierarchical arrangement.

The problem is that I do not know the "depth" (i.e. the number of keys). If I have a constant number, I could do something like:

 %h{$inner_hash{$PRIMARY_KEY}}{$inner_hash{$SECONDARY_KEY}}{...} = filter_copy($inner_hash,[$PRIMARY_KEY,$SECONDARY_KEY]) 

so maybe I can write a loop that will add one level at a time, remove this key from the hash, than add the remaining hash to the "current" location, but it's a little cumbersome, and I'm not sure how to save the "location" in the hash hashes ...

+4
source share
2 answers
 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)); 

According to @FM's comment, you really need an extra array level.

Output:

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

EDIT: Oh, BTW - if anyone knows how to gracefully clone link contents, please teach. Thanks!

EDIT EDIT: @FM helped. Now everything is better: D

+6
source

As you already understood, writing code to create hash structures of arbitrary depth is a bit difficult. And the access code to such structures is equally complex. It makes you wonder: Do you really want to do this?

A simpler approach may be to put the source information in a database. As long as the keys you care about are indexed, the DB engine can quickly get the lines of interest: Give me all the faces where SEX = woman and NATIONALITY = Swedish. Now that sounds promising!

You can also find this interesting question .

+2
source

All Articles