Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Assign (key, value) to a hash w/o clobbering hash

by Melly (Hermit)
on Sep 28, 2006 at 08:16 UTC ( #575300=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monkees,

Is there anyway to assign a 2-value list as a key/value pair to a hash without clobbering the current keys/values in hash, and without excessive code? - e.g.

# Works, but needs double-split $hash{(split /X/, "fooXbar")[0]} = (split /X/, "fooXbar")[1]; $hash{(split /X/, "bishXbash")[0]} = (split /X/, "bishXbash")[1]; foreach(keys %hash){ print "1: $_ = $hash{$_}\n"; } # Works, but needs redundent array undef %hash; @array = (split /X/, "fooXbar"); $hash{$array[0]} = $array[1]; @array = (split /X/, "bishXbash"); $hash{$array[0]} = $array[1]; foreach(keys %hash){ print "2: $_ = $hash{$_}\n"; } # Tidy, but clobbers {foo} = 'bar' undef %hash; %hash = (split /X/, "fooXbar"); %hash = (split /X/, "bishXbash"); foreach(keys %hash){ print "3: $_ = $hash{$_}\n"; }

So, the first two approaches work, but IMHO look ugly. The third approach looks concise, but clobbers the hash each time. Hope that makes sense...

Tom Melly, tom@tomandlu.co.uk

Replies are listed 'Best First'.
Re: Assign (key, value) to a hash w/o clobbering hash
by davorg (Chancellor) on Sep 28, 2006 at 08:23 UTC

    Are you looking for something like:

    %hash = (%hash, split /X/ 'fooXbar');

    I don't think that's particularly efficient tho' as it rewrites the entire hash every time. I'd probably use your second solution and split to an intermediate array.

    --
    <http://dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

      To avoid this inefficiency, you could always tie your own hash which didn't clobber the original values, by not implementing CLEAR. So your above line becomes.
      %hash = split /X/, 'fooXbar' # added the missing comma ;)
      depends how far you want to go. Full example as follows...

      (BTW I did try to do this with Tie::StdHash, but couldn't figure it out)

      #!/usr/bin/perl use strict; use Data::Dumper; tie my %hash, 'ExpandingHash'; $hash{'still'} = 'here'; %hash = split /X/, 'fooXbar'; print Dumper(\%hash); # I also added a new clear method, which you could use like this. # tied(%hash)->FORCE_CLEAR; __OUTPUT__ $VAR1 = { 'foo' => 'bar', 'still' => 'here' }; #================================================================== package ExpandingHash; use strict; sub TIEHASH { my %self; bless \%self, shift } sub FETCH { $_[0]->{$_[1]} } sub STORE { $_[0]->{$_[1]} = $_[2] } sub FIRSTKEY { each %{$_[0]} } sub NEXTKEY { each %{$_[0]} } sub CLEAR { } # NOT IMPLEMENTED sub DELETE { delete $_[0]->{$_[1]} } sub EXISTS { exists $_[0]->{$_[1]} } sub FORCE_CLEAR { %{$_[0]} = () }
      ---
      my name's not Keith, and I'm not reasonable.

        That's nasty :)

        It's much simpler using Tie::StdHash as you only need to implement the methods that you want to override.

        package Tie::Hash::ExpandingHash; use strict; use warnings; use Tie::Hash; our @ISA = 'Tie::StdHash'; sub CLEAR { } # NOT IMPLEMENTED sub FORCE_CLEAR { %{$_[0]} = () }
        --
        <http://dave.org.uk>

        "The first rule of Perl club is you do not talk about Perl club."
        -- Chip Salzenberg

Re: Assign (key, value) to a hash w/o clobbering hash
by Corion (Patriarch) on Sep 28, 2006 at 08:23 UTC

    When merging two hashes, I mostly recreate the merged hash from the two other hashes:

    my %actual = (%defaults,%args);

    The order of %hash1 and %hash2 determines, which keys/values take precedence - most times, I want the defaults to have lower precedence than the supplied parameters. Of course, here, clobbering is what is wanted. I'm not aware of a callback-based solution to merge hashes like reduce does for lists.

Re: Assign (key, value) to a hash w/o clobbering hash
by johngg (Canon) on Sep 28, 2006 at 08:53 UTC
    You can set the hash up at the beginning from multiple strings using map.

    perl -e '%h = map{split /X/} q{fooXbar}, q{bishXbash}; print qq{$_ -> $h{$_}\n} for jeys %h;'

    produces

    bish -> bash foo -> bar

    If you have to add another key/value pair subsequently you will have to use intermediate variables to avoid the clobber.

    Cheers,

    JohnGG

Re: Assign (key, value) to a hash w/o clobbering hash
by cdarke (Prior) on Sep 28, 2006 at 09:57 UTC
    The second solution gets my vote, but if you are worried about the extra array, just make it a 'my' variable inside a set of braces. If you are doing this several time in your code:
    sub add2hash { my ($value, $hashref) = @_; my @array = (split /X/, $value); $hashref->{$array[0]} = $array[1]; } ... add2hash ('fooXbar', \%hash); add2hash ('bishXbash', \%hash);
    OK, possibly a sledge hammer to crack a nut, depends on how many time you are doing it.
    update: corrected missing $
Re: Assign (key, value) to a hash w/o clobbering hash
by rjray (Chaplain) on Sep 28, 2006 at 08:34 UTC

    How about:

    @X_delimited_strings = qw(fooXbar bishXbash); for (@X_delimited_strings) { @_ = split /X/; $hash{$_[0]} = $_[1]; }

    You will have to accept a temp variable somewhere. You can use those already available to you (like $_ and @_) or declare your own.

    --rjray

Re: Assign (key, value) to a hash w/o clobbering hash
by ikegami (Patriarch) on Sep 28, 2006 at 14:21 UTC
    You can also use a hash slice.
    %tmphash = map { split /X/ } "fooXbar", "bishXbash"; @hash{keys %tmphash} = values %tmphash;
Re: Assign (key, value) to a hash w/o clobbering hash
by jdporter (Chancellor) on Sep 28, 2006 at 14:19 UTC
    my $kv1 = 'fooXbar'; my $kv2 = 'bishXbash'; @hash{ keys %$_ } = values %$_ for { map { split /X/ } $kv1, $kv2 };

    Don't be fooled by the for loop: it only iterates once; it's only there to avoid the explicit temporary variable (hash).

    We're building the house of the future together.
Re: Assign (key, value) to a hash w/o clobbering hash
by TedPride (Priest) on Sep 28, 2006 at 18:51 UTC
    use Data::Dumper; $hash{bing} = 'bang'; for (qw/fooXbar bishXbash/) { @_ = split /X/, $_; $hash{$_[0]} = $_[1]; } print Dumper(\%hash);
Re: Assign (key, value) to a hash w/o clobbering hash
by wazoox (Prior) on Sep 28, 2006 at 13:19 UTC
    Why not using map? This is concise and uses no temporary variable :
    %hash = map { split( /X/, $_ ) } ( "fooXbar","bishXbash") ; # look Ma, that works! foreach(keys %hash){ print "1: $_ = $hash{$_}\n"; }
      because the OP doesn't want to clobber what's already in the hash. Assigning a list to a hash clears out the hash first, and then starts assigning the new values.

      Update: argh, see mreece's reply to this post

      Regardless, the map is completely redundant here, the following two are equivalent.

      %hash = map { split( /X/, $_ ) } ( "fooXbar","bishXbash"); %hash = split /X/, ("fooXbar","bishXbash");
      ---
      my name's not Keith, and I'm not reasonable.
        those are not at all equivalent!
        DB<1> x %hash = map { split( /X/, $_ ) } ( "fooXbar","bishXbash"); 0 'foo' 1 'bar' 2 'bish' 3 'bash' DB<2> x %hash = split /X/, ("fooXbar","bishXbash"); 0 'bish' 1 'bash'
        because the OP doesn't want to clobber what's already in the hash.

        Maybe, but the OP proposed a solution that do reset the hash :

        # Works, but needs redundent array undef %hash; @array = (split /X/, "fooXbar"); $hash{$array[0]} = $array[1]; @array = (split /X/, "bishXbash"); $hash{$array[0]} = $array[1]; foreach(keys %hash){ print "2: $_ = $hash{$_}\n"; }

        so what is to be achieved isn't that clear after all.

Re: Assign (key, value) to a hash w/o clobbering hash
by Khen1950fx (Canon) on Sep 28, 2006 at 10:59 UTC
    I gave your third approach a try. I tried this:

    undef %hash %hash = (%hash, split /X/ 'fooXbar'); %hash = (%hash, split /X/ 'bishXbash'); foreach(keys %hash) { print "3: $_ = $hash{$_}\n"; }

    It produced:

    bish = bash foo = bar

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://575300]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (5)
As of 2022-06-28 06:29 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My most frequent journeys are powered by:









    Results (90 votes). Check out past polls.

    Notices?