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

I have two hashes %hash1,%hash2 - each one of them has a key (represents the path) and a value (represents a numeric value).
I would like to merge those two hashes into one main hash so every main hash key is the sum of the two values (of the same keys).
if $key1 of %hash1 with value1 is equal to $key2 of %hash2 with value 2 then the value of %main_hash{$key1} is value1+value2.
How should I do it? I already checked that the value is numeric so no need to check. Looking for the best-shorted way :)

EDIT: sorry If my question was hard to understand. Each one of the hashes can containe keys that the other hash doesn't have. I think I know how to implement it by a function but I was wondering if there is a better, good looking, less coded way.
Again, sorry for not explaining the question enough.

Replies are listed 'Best First'.
Re: merge two hashes into one
by choroba (Cardinal) on May 29, 2018 at 19:05 UTC
    Initialise the new hash with one of the old ones, so you can only iterate over the other one:
    #!/usr/bin/perl use warnings; use strict; my %hash1 = (a => 12, b => 15); my %hash2 = (a => 30, c => 17); my %merged = %hash1; $merged{$_} += $hash2{$_} for keys %hash2; use Data::Dumper; print Dumper \%merged;
    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: merge two hashes into one
by hippo (Archbishop) on May 29, 2018 at 17:49 UTC
Re: merge two hashes into one
by AnomalousMonk (Archbishop) on May 29, 2018 at 17:12 UTC

    c:\@Work\Perl>perl -wMstrict -MData::Dump -le "my %h1 = qw(foo 3 bar 99); my %h2 = qw(foo 5 bar 87); ;; die 'hashes unequal: merging invalid' if keys %h1 != keys %h2; ;; for my $k (keys %h1) { die qq{key '$k' not found: merging invalid} unless exists $h2{$k}; $h1{$k} += $h2{$k} } ;; dd \%h1; " { bar => 186, foo => 8 }

    Update: Hey, isn't this a FAQ somewhere? Can't find it ATM.


    Give a man a fish:  <%-{-{-{-<

Re: merge two hashes into one
by stevieb (Canon) on May 29, 2018 at 17:19 UTC

    map is useful here. The following example assumes that keys in both hashes are the same; ie. %h2 does not have any extra keys that %h1 doesn't have.

    use strict; use Data::Dumper; my %h1 = (a => 1, b => 2, c => 3); my %h2 = (a => 6, b => 8, c => 0); my %h3 = map {$_ => $h1{$_} + $h2{$_}} keys %h1; print Dumper \%h3;

    Output:

    $VAR1 = { 'c' => 3, 'b' => 10, 'a' => 7 };

    If %h2 does have keys that %h1 doesn't have, change this line:

    my %h3 = map {$_ => $h1{$_} + $h2{$_}} keys %h1;

    ...to this:

    my %h3 = map {$_ => $h1{$_} + $h2{$_}} keys %h1, keys %h2;

      map could also be used to generate the sums of the intersection set of the two hashes (with no exceptions thrown):

      c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "my %h1 = qw(a 1 b 2 c 3 ); my %h2 = qw( b 7 c 8 d 4); ;; my %h_out = map exists $h2{$_} ? ($_ => $h1{$_} + $h2{$_}) : (), keys %h1 ; dd \%h_out; " { b => 9, c => 11 }
      The exists test could be moved into a separate grep step for perhaps greater clarity, but I'd expect a bit of a performance hit with large hashes.

      BTW: Won't
          my %h3 = map {$_ => $h1{$_} + $h2{$_}} keys %h1, keys %h2;
      cause "Use of uninitialized value ..." warnings unless that warning is disabled?


      Give a man a fish:  <%-{-{-{-<

        my %h1 = qw( a 1 b 2 c 3 ); my %h2 = qw( b 7 c 8 d 4 ); my %h3 = map { $_ => $h1{$_} // 0 + $h2{$_} // 0 } keys %h1, keys %h2;

        No warnings, merges both hashes


        Enjoy, Have FUN! H.Merijn
Re: merge two hashes into one
by jdporter (Paladin) on May 30, 2018 at 17:06 UTC

    My solution, designed to take any number of hashes, not just two. Encapsulated in a function named add_hashes. Uses some functions in List::Util.

    use List::Util qw( sum uniq ); # pass list of hashrefs. # returns a hash (list). sub add_hashes(@) { map { my $k = $_; $k => sum( map $_->{$k}, @_ ) } uniq( map keys(%$_), @_ ) }
    Test it:
    my %h1 = map { ( $_ => 1 ) } qw( a b c d e f g h ); my %h2 = map { ( $_ => 1 ) } qw( b c d e f g h i ); my %h3 = map { ( $_ => 1 ) } qw( c d e f g h i j ); my %h4 = map { ( $_ => 1 ) } qw( d e f g h i j k ); my %summed = add_hashes( \%h1, \%h2, \%h3, \%h4, ); use Data::Dumper; $Data::Dumper::Sortkeys=1; print Dumper \%summed;
Re: merge two hashes into one
by Veltro (Hermit) on May 29, 2018 at 20:02 UTC

    ;)

    use strict ; use warnings ; use Data::Dumper ; my $h1 = { a => 1, b => 2 } ; my $h2 = { a => 1, c => 3 } ; foreach( keys %{ $h2 } ) { merge( $h1 )->( $_ ) += $h2->{ $_ } ; } print Dumper( $h1 ) ; sub merge : lvalue { my $inh = $_[0] ; my $ret = sub : lvalue { $inh->{ $_[0] } ; } ; $ret ; }
Re: merge two hashes into one
by AnomalousMonk (Archbishop) on May 30, 2018 at 01:30 UTC
    [From the OP update:] Each one of the hashes can containe keys that the other hash doesn't have.

    And what should happen then? How should a key that exists in only one hash be handled?


    Give a man a fish:  <%-{-{-{-<

Re: merge two hashes into one
by Anonymous Monk on May 30, 2018 at 19:46 UTC

    Slight obscura; please no mock my style! ;-)

    use List::Util qw( reduce pairs ); # Firm talk so perl know I mean it. reduce ## abuce { $$a { $$b[0] } += $$b[1],$a } \ # It simple now. my %result => pairs %h1, %h2, %h3, %h4;

Re: merge two hashes into one
by locked_user sundialsvc4 (Abbot) on May 29, 2018 at 22:20 UTC

    Some of the offered answers seem wrong to me.   If you want to add an element to the output hash only if the key exists in both inputs, I think that you would need something that is essentially this (tested) logic:

    use strict; use warnings; use Data::Dumper; my $h1 = { 'a' => 1, 'b' => 2, 'c' => 3 }; my $h2 = { 'b' => 2, 'c' => 4, 'd' => 5 }; my $hout = {}; foreach my $k (keys $h1) { if (exists $h2->{$k}) { $hout->{$k} = $h1->{$k} + $h2->{$k}; } } print Data::Dumper->Dump([$hout], ['hout']); $hout = { 'b' => 4, 'c' => 7 };
    Loop through all of the keys in one hash and check to see if this key also exists in the other.   If so, populate the output hash with the sum of the two.

    Looking through the various answers, it seemed to me that some either modified $h1, or assumed that the key existed in $h2, or relied upon side effects.   (Of course I did not down-vote any of them – I never do – and perhaps I am quite mistaken.)

    Now, as far as “a good-looking, less-coded way,” the only criteria that really matters to me is that the logic is tested, that it is “at-a-glance obvious to anyone and everyone,” and that it is easy to maintain.   If a future program-change requirement is to do different or additional things to the matching elements, it should be easy to make that modification without breaking what’s there.   I place no premium on brevity – quite the opposite.   (My career experience commonly deals with software that is dozens of years old and built by a great many someone-elses, so I am very keen to such concerns.)

      From the doc for keys:

      Starting with Perl 5.14, an experimental feature allowed keys to take a scalar expression. This experiment has been deemed unsuccessful, and was removed as of Perl 5.24.

      So when you "tested" this the very best possible result was that you saw the "keys on reference is experimental" warning and blithely ignored it. Otherwise your code didn't compile at all and you ignored that.

        So when you "tested" this ... you saw the "keys on reference is experimental" warning .... Otherwise your code didn't compile at all ...

        I don't know what Perl version sundialsvc4 was using, but I tested the code under Perl 5.14 and it works as advertised.

        I and many others have long had a problem with sundialsvc4: he so often just doesn't seem to know what the hell he's talking about. Even when I occasionally encounter a post of his that seems, at first glance, to make a valid point, I have gotten into the habit of simply passing on by: I've been bitten too often by bugs and gotchas that only become apparent on closer inspection or through the replies of others to risk upvoting.

        Lately, I've seen a couple of his posts that are so clear, straighforward and helpful that I've upvoted them; this is one. Of course, a note about the "highly experimental" nature of  keys EXPR would have been nice and there's still a bit of bloviation, but you can't have everything. There's code, compilable, runnable code that works as described, and a few valid points! sundialsvc4 takes up so much bandwidth on this site that it's a shame his contributions haven't been correspondingly worthwhile. Is a new day dawning? I hope so, and I think any effort in that direction is worth encouraging.


        Give a man a fish:  <%-{-{-{-<