http://www.perlmonks.org?node_id=11113143

Sami_R has asked for the wisdom of the Perl Monks concerning the following question:

Morning Monks,

I have two hash and trying to join into one hash

#!/usr/bin/perl -w use strict; use warnings; my $VAR1 = { 'Total' => { 'month1' => 0, 'month2' => 0, 'month3' => 0, 'month4' => 0, 'month5' => 0, 'month6' => 0, 'month7' => 0, 'month8' => 0, 'month9' => 0, 'month10' => 0, 'month11' => 0, 'month13' => 0, 'month14' => 0, 'month15' => 0, 'month16' => 0 }, 'Tom' => { 'month1' => 17, 'month2' => 1, 'month3' => 15, 'month4' => 0, 'month5' => 3, 'month6' => 30, 'month7' => 33, 'month8' => 0, 'month9' => 0, 'month10' => 0, 'month11' => 0, 'month12' => 0, 'month13' => 0, 'month14' => 0, 'month15' => 0 } }; my $VAR2 = [ { 'Total' => { 'week1' => 0, 'week2' => 0, 'week3' => 0 }, 'Harry' => { 'week1' => 0, 'week2' => 5, 'week3' => 5 } } ]; my %joined_FS; $joined_FS{$_} ||= {( %{$VAR1->{$_}||{}}, %{$VAR2->{$_}||{}} )} fo +r keys(%$VAR1), keys(%$VAR2); #This example was given by PerlMonks print Dumper(\%joined_FS);
#Error: Not a HASH reference at perl_script_work2.pl line 2441. # Looks like your test exited with 255 just after 2.

Expected Output:

$VAR1 = { 'Tom' => { 'month1' => 17, 'month2' => 1, 'month3' => 15, 'month4' => 0, 'month5' => 3, 'month6' => 30, 'month7' => 33, 'month8' => 0, 'month9' => 0, 'month10' => 0, 'month11' => 0, 'month12' => 0, 'month13' => 0, 'month14' => 0, 'month15' => 0 }, 'Total' => { 'month1' => 0, 'month2' => 0, 'month3' => 0, 'month4' => 0, 'month5' => 0, 'month6' => 0, 'month7' => 0, 'month8' => 0, 'month9' => 0, 'month10' => 0, 'month11' => 0, 'month13' => 0, 'month14' => 0, 'month15' => 0, 'month16' => 0, 'week1' => 0, 'week2' => 0, 'week3' => 0 }, 'Harry' => { 'week1' => 0, 'week2' => 5, 'week3' => 5 } };

Please give me directions to fix this, many thanks.

Replies are listed 'Best First'.
Re: Error: when joining two hashes
by Laurent_R (Canon) on Feb 19, 2020 at 10:33 UTC
    my $VAR2 = [ ...
    is defined as an array ref, not a hash ref.

      Thanks. If its a array ref: when I tried ...

      $joined_FS{$_} ||= {( %{$VAR1->{$_}||{}}, @{$VAR2->{@_}||{}} )} for ke +ys(%$VAR1), keys(@$VAR2);

      I get same error ... Is it possible to join array ref and hash ref please?

        You can't call keys on an array so that's not going to do anything useful. There's a slight whiff of an XY problem in that your data in the two variables isn't of the same shape (HoH vs AoHoH); it might help if you step back a notch and figure out why your previous step(s) aren't making the same outputs.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

        I agree with Fletch that it seems a bit odd that your two variables don't have the same data structure. $VAR1 is a hash ref (or more precisely a reference to a hash of hashes), but $VAR2 is an array ref (a reference to an array of hashes of hashes), but there is no obvious reason in the data that you show to have an array as the top data structure. Is this data structure generated by your program at some earlier point? The proper fix would probably be to correct the part of the program that generates $VAR2, but you don't show that part.
Re: Error: when joining two hashes
by haukex (Archbishop) on Feb 19, 2020 at 11:31 UTC

    I'm making some assumptions about how you want the data merged, but at least the following produces your expected output:

    use Hash::Merge; my $merger = Hash::Merge->new(); my $joined_FS = {}; $joined_FS = $merger->merge($joined_FS, $_) for $VAR1, @$VAR2;

      Thank you so much Haukex, that really solved my issue. Thanks again.

Re: Error: when joining two hashes
by kcott (Archbishop) on Feb 20, 2020 at 08:28 UTC

    G'day Sami_R,

    Here's one way to do it. I've minimised your data for demonstration purposes.

    $ perl -e ' use strict; use warnings; use Data::Dumper; my $VAR1 = { Total => { month1 => 0, month2 => 0 }, Tom => { month1 => 17, month2 => 1 }, }; my $VAR2 = [ { Total => { week1 => 0, week2 => 0 }, Harry => { week1 => 0, week2 => 5 }, } ]; my %joined_FS = ( Total => { %{delete $VAR1->{Total}}, %{delete $VAR2->[0]{Total}}, }, %$VAR1, %{$VAR2->[0]} ); print Dumper(\%joined_FS); ' $VAR1 = { 'Tom' => { 'month1' => 17, 'month2' => 1 }, 'Total' => { 'month2' => 0, 'month1' => 0, 'week2' => 0, 'week1' => 0 }, 'Harry' => { 'week2' => 5, 'week1' => 0 } };

    — Ken

      Thanks Ken, that really worked.

Re: Error: when joining two hashes
by BillKSmith (Monsignor) on Feb 19, 2020 at 16:09 UTC
    Your code would work as expected if $VAR2 were a reference to a single hash rather than a reference to an array with one element (a hash ref).
    #!/usr/bin/perl -w use strict; use warnings; use Data::Dumper; my $VAR1 = { 'Total' => { 'month1' => 0, 'month2' => 0, 'month3' => 0, 'month4' => 0, 'month5' => 0, 'month6' => 0, 'month7' => 0, 'month8' => 0, 'month9' => 0, 'month10' => 0, 'month11' => 0, 'month13' => 0, 'month14' => 0, 'month15' => 0, 'month16' => 0 }, 'Tom' => { 'month1' => 17, 'month2' => 1, 'month3' => 15, 'month4' => 0, 'month5' => 3, 'month6' => 30, 'month7' => 33, 'month8' => 0, 'month9' => 0, 'month10' => 0, 'month11' => 0, 'month12' => 0, 'month13' => 0, 'month14' => 0, 'month15' => 0 } }; my $VAR2 = { 'Total' => { 'week1' => 0, 'week2' => 0, 'week3' => 0 }, 'Harry' => { 'week1' => 0, 'week2' => 5, 'week3' => 5 } }; my %joined_FS; $joined_FS{$_} ||= {( %{$VAR1->{$_}||{}}, %{$VAR2->{$_}||{}} )} fo +r keys(%$VAR1), keys(%$VAR2); #This example was given by PerlMonks print Dumper(\%joined_FS); OUTPUT: $VAR1 = { 'Tom' => { 'month10' => 0, 'month11' => 0, 'month15' => 0, 'month6' => 30, 'month7' => 33, 'month5' => 3, 'month13' => 0, 'month12' => 0, 'month4' => 0, 'month9' => 0, 'month3' => 15, 'month1' => 17, 'month2' => 1, 'month8' => 0, 'month14' => 0 }, 'Total' => { 'week3' => 0, 'month6' => 0, 'week1' => 0, 'month10' => 0, 'month8' => 0, 'month2' => 0, 'month4' => 0, 'month5' => 0, 'month7' => 0, 'month15' => 0, 'month11' => 0, 'month14' => 0, 'month16' => 0, 'month1' => 0, 'month3' => 0, 'month9' => 0, 'month13' => 0, 'week2' => 0 }, 'Harry' => { 'week3' => 5, 'week1' => 0, 'week2' => 5 } };
    Bill

      $VAR1 is HoH = Hash of Hash.
      $VAR2 is AoHoH = Array of Hash of Hash.

      Trying explain. Please correct me if i am wrong. It seems like Sami is trying to merge two hashes. $VAR1 is within HASH and $VAR2 is within ARRAY but no changes to its' key value. This code is to explain only. it's not perfect.
      my %joined_FS; my %outer_keys; for my $var (($VAR1, $VAR2)) { if (ref $var eq "HASH") { while (my($key, $value) = each %$VAR1) { $outer_keys{$key} = $value; } } elsif (ref $var eq "ARRAY") { for my $href (@$var) { my @unq_keys = keys %$href; for my $key (@unq_keys) { if (!exists $outer_keys{$key}) { $outer_keys{$key} = $href->{$key}; } else { while (my($k, $v) = each %{$href->{$key}}) { $outer_keys{$key}->{$k} = $v; } } } } } } print Dumper \%outer_keys;
        I showed that the original code would correctly merge two hashes. You have probably described Sami_R's problem correctly. In that case, all that is needed is another layer of indexing to correctly reference the second hash ($VAR2->[0] instead of $VAR2).
        #!/usr/bin/perl -w use strict; use warnings; use Test::More tests=>1; my $VAR1 = { 'Total' => { 'month1' => 0, 'month2' => 0, 'month3' => 0, 'month4' => 0, 'month5' => 0, 'month6' => 0, 'month7' => 0, 'month8' => 0, 'month9' => 0, 'month10' => 0, 'month11' => 0, 'month13' => 0, 'month14' => 0, 'month15' => 0, 'month16' => 0 }, 'Tom' => { 'month1' => 17, 'month2' => 1, 'month3' => 15, 'month4' => 0, 'month5' => 3, 'month6' => 30, 'month7' => 33, 'month8' => 0, 'month9' => 0, 'month10' => 0, 'month11' => 0, 'month12' => 0, 'month13' => 0, 'month14' => 0, 'month15' => 0 } }; my $VAR2 = [ { 'Total' => { 'week1' => 0, 'week2' => 0, 'week3' => 0 }, 'Harry' => { 'week1' => 0, 'week2' => 5, 'week3' => 5 } } ]; my $EXPECTED = { 'Tom' => { 'month1' => 17, 'month2' => 1, 'month3' => 15, 'month4' => 0, 'month5' => 3, 'month6' => 30, 'month7' => 33, 'month8' => 0, 'month9' => 0, 'month10' => 0, 'month11' => 0, 'month12' => 0, 'month13' => 0, 'month14' => 0, 'month15' => 0 }, 'Total' => { 'month1' => 0, 'month2' => 0, 'month3' => 0, 'month4' => 0, 'month5' => 0, 'month6' => 0, 'month7' => 0, 'month8' => 0, 'month9' => 0, 'month10' => 0, 'month11' => 0, 'month13' => 0, 'month14' => 0, 'month15' => 0, 'month16' => 0, 'week1' => 0, 'week2' => 0, 'week3' => 0 }, 'Harry' => { 'week1' => 0, 'week2' => 5, 'week3' => 5 } }; my %joined_FS; $joined_FS{$_} ||= { ( %{$VAR1->{$_}||{}}, %{$VAR2->[0]->{$_}||{}} ) } for keys(%$VAR1), keys(%{$VAR2->[0]}); is_deeply(\%joined_FS, $EXPECTED, 'merge');

        OUTPUT:

        1..1 ok 1 - merge
        Bill