views:

57

answers:

2

I write a sub in Perl to merge 2 hashes of the same structure; so that merge($a,$b)

$a = {
 k1 => { sk1 => 'v1' },
 k2 => { sk3 => 'v3', sk4 => 'v4' }
};
$b = {
 k1 => { sk2 => 'v2'},
 k3 => { sk5 => 'v5'} 
};

would result in

$c = {
 k1 => { sk1 => 'v1', sk2 => 'v2' },
 k2 => { sk3 => 'v3', sk4 => 'v4' }
 k3 => { sk5 => 'v5'} 
};

Below is my code for merge, and it does not work. How can I correct it? Thank you.

sub merge {
 my ($old,$new) = @_;
 foreach my $k (keys($old)) {
  if (exists $new->{$k}) {
   if (ref($old->{$k}) eq 'HASH') {
    merge($old->{$k},$new->{$k});
   } else {
    $new->{$k} = $old->{$k};
   } 
  } else { 
   $new->{$k} = $old->{$k};
  }
 }
 return $new;
}
+4  A: 

Unless you're doing this just to learn how it's done, I'd use a premade solution like Hash::Merge or Hash::Merge::Simple.

bemace
Oh, I didn't know it exists. Thanks
Martin
+1  A: 

This should be plenty good enough:

for my $href ($a, $b) {
    while (my($k,$v) = each %$href) {
        $c->{$k} = $v;
    }
}

If you don't want to clobber duplicates, perhaps because you're concerned about ordering issues, instead use:

for my $href ($a, $b) {
    while (my($k,$v) = each %$href) {
        push @{ $c->{$k} },  $v;
    }
}

The advantage of doing this very simple operation yourself is it helps develop facility with basic Perl data structures, something critical on the path to fluency in the language.

Note however that these are surface copies, so references will be shared. It's not a deep copy.

I used each instead of keys so that it scales in case you use DBM hashes with millions of elements.

tchrist