Skip Menu |

This queue is for tickets about the Hash-Merge-Simple CPAN distribution.

Report information
The Basics
Id: 41738
Status: resolved
Priority: 0/
Queue: Hash-Merge-Simple

People
Owner: Nobody in particular
Requestors: uri [...] stemsystems.com
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: questions and optimizations
Date: Mon, 15 Dec 2008 19:50:59 -0500
To: bug-hash-merge-simple [...] rt.cpan.org
From: Uri Guttman <uri [...] stemsystems.com>
hi, i just found hash::merge::simple and i have some comments. i think there is a subtle bug in how the hashes are copied/merged. the left hash is copied at the top level but the lower level refs are the same as in the left side. so if you modify left deep down after you merged over it, it will modify both the left and the merged. the returned hash ref is not the same as left but its guts may be shared still. so either fix this or document that this is the behavior. you aren't actually overlaying on left but it gives the impression you are getting a complete copy of the merging. below is quick test which shows this. i intend to use this but i will have to do deep copies (which i already have) before i do the merge. my data is filtered later on and if it is shared between higher level things that could me filtering of filtered data which is very bad. so knowing whether your module does deep copies is important. also i found some minor optimizations. my $hr = (ref $right->{$key} || '') eq 'HASH'; my $hl = ((exists $left->{$key} && ref $left->{$key}) || '') eq 'HASH'; if ($hr and $hl){ ref works on undef values and doesn't generate a warning. and accessing a missing key a the top level of a hash will not autovivify it so you don't need the exists call either. the first two lines can be reduced to: my $hr = ref $right->{$key} eq 'HASH'; my $hl = ref $left->{$key} eq 'HASH'; and that can be reduced to: my( $hr, $hl ) = map ref $_->{$key} eq 'HASH', $right, $left ; or not. :) so i will be copying the guts of this and making sure i do a deep copy before i merge. thanx, uri # this imported (actually pasted :) merge(). note the output shows that # the top level foo hash is copied (the added aaa is only in left) but # the 'bar' sub hash is modified in both left and merged. use Data::Dumper ; my $left = { foo => { bar => 2 } } ; my $right = { baz => 4 } ; my $merged = merge( $left, $right ) ; print Dumper $left, $merged ; $left->{foo}{bar} = 3 ; $left->{foo}{aaa} = 5 ; print Dumper $left, $merged ; -- Uri Guttman ------ uri@stemsystems.com -------- http://www.sysarch.com -- ----- Perl Code Review , Architecture, Development, Training, Support ------ --------- Free Perl Training --- http://perlhunter.com/college.html --------- --------- Gourmet Hot Cocoa Mix ---- http://bestfriendscocoa.com ---------
On Mon Dec 15 20:00:49 2008, uri@stemsystems.com wrote: Show quoted text
> > hi, > > i just found hash::merge::simple and i have some comments. > > i think there is a subtle bug in how the hashes are copied/merged. the > left hash is copied at the top level but the lower level refs are the > same as in the left side. so if you modify left deep down after you > merged over it, it will modify both the left and the merged. > the returned hash ref is not the same as left but its guts may be > shared > still. so either fix this or document that this is the behavior. you > aren't actually overlaying on left but it gives the impression you are > getting a complete copy of the merging. below is quick test which > shows > this. > > i intend to use this but i will have to do deep copies (which i > already > have) before i do the merge. my data is filtered later on and if it is > shared between higher level things that could me filtering of filtered > data which is very bad. so knowing whether your module does deep > copies > is important. > > also i found some minor optimizations. > > my $hr = (ref $right->{$key} || '') eq 'HASH'; > my $hl = ((exists $left->{$key} && ref $left->{$key}) || '') > eq 'HASH'; > > if ($hr and $hl){ > > ref works on undef values and doesn't generate a warning. and > accessing > a missing key a the top level of a hash will not autovivify it so you > don't need the exists call either. the first two lines can be reduced > to: > > my $hr = ref $right->{$key} eq 'HASH'; > my $hl = ref $left->{$key} eq 'HASH'; > > and that can be reduced to: > > my( $hr, $hl ) = map ref $_->{$key} eq 'HASH', $right, $left ; > > or not. :) > > so i will be copying the guts of this and making sure i do a deep copy > before i merge. > > thanx, > > uri > > # this imported (actually pasted :) merge(). note the output shows > that > # the top level foo hash is copied (the added aaa is only in left) but > # the 'bar' sub hash is modified in both left and merged. > > use Data::Dumper ; > > my $left = { foo => { bar => 2 } } ; > my $right = { baz => 4 } ; > > my $merged = merge( $left, $right ) ; > > print Dumper $left, $merged ; > > $left->{foo}{bar} = 3 ; > $left->{foo}{aaa} = 5 ; > > print Dumper $left, $merged ; > >
Thanks, I've incorporated all your suggestions I've kept existing functionality as-is, and added clone_merge and dclone_merge methods Rob