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

Sorry if this is a dim question - I've tried various permutations of dereferencing and I can't make it go. If I want to merge %old into %new, I'd use a hashref:
@old{keys %new} = values %new;
But what if, instead of hashes I have hash references? In my case this happens because I'm merging the output of two DBI calls, having fetched my data by doing $sth->fetchrow_hashrefs. I could just do
my %old = %$old; my %new = %$new;
and then proceed as before; but that seems like a needless proliferation of data structures. And in fact, having written this out, I realise I could also use $sth->fetchrow_array to get my data from the db, and then create a single new hash to store the elements from my two new hashes.

But that still feels like too many steps. Is there a syntax for doing hash slice operations on hashrefs? Or some other one liner to merge two hashrefs?

§ George Sherston

Replies are listed 'Best First'.
Re: Slicing a hashref
by Juerd (Abbot) on Jan 19, 2002 at 19:42 UTC
    Go see Hash::Merge - as the name says, it can merge hashes :)

    Hash %foo          Reference $foo to a hash
    
    %foo               %{$foo}, %$foo
    @foo{qw/foo bar/}  @{$foo}{qw/foo bar/}, @$foo{qw/foo bar/}
    $foo{bar}          ${$foo}{bar}, $$foo{bar}, $foo->{bar}
    
    (Note: $foo->{bar} notation is prefered when accessing a single value)

    hth

    2;0 juerd@ouranos:~$ perl -e'undef christmas' Segmentation fault 2;139 juerd@ouranos:~$

Re (tilly) 1: Slicing a hashref
by tilly (Archbishop) on Jan 19, 2002 at 20:52 UTC
    If $new and $old are hashrefs, then you can still do the slice as follows:
    @$old{keys %$new} = values %$new;
    Just remember that the $ is part of the name of the hash, and it should make sense. :-)
Re: Slicing a hashref
by broquaint (Abbot) on Jan 19, 2002 at 19:47 UTC
    To use a hash slice, just dereference the hash like so
    @$hashref{keys %some_hash} = values %some_hash;
    Or to grab a single row from a database you could do this
    my %data = %{$dbh->selectall_hashref()->[0]};
    Of course this assumes you're only selecting one row from the database and is only really for demonstrative purposes.
    HTH

    broquaint

Re: Slicing a hashref
by trs80 (Priest) on Jan 19, 2002 at 20:27 UTC
    I am not entirely sure what data you have since you are
    mentioning DBI code as well. If you are merging two query
    results would it be possible to merge the query rather then
    the results?

    Anyway as for merging two hashrefs, here is my code for that

    $hash1 = { a => '56', b => '75', c => '87' }; $hash2 = { d => '45', f => '46', g => '67', }; my $hash3 = { %$hash1, %$hash2 }; foreach (keys %$hash3) { print "$_ = $hash3->{$_}\n"; }
      I started wondering how effcient my solution might be since Perl is not as efficient with hashes as it is arrays. So I compared it to what tilly posted.

      @$old{keys %$new} = values %$new;
      I did a benchmark on each block of code.

      Benchmark: timing 1000000 iterations of tilly_way, trs80_way...
       tilly_way: 11 wallclock secs 
          (10.27 usr +  0.00 sys = 10.27 CPU) @ 97408.92/s (n=1000000)
       trs80_way: 26 wallclock secs 
          (24.16 usr +  0.00 sys = 24.16 CPU) @ 41382.16/s (n=1000000)
      

      So don't do it the trs80_way for two reasons. One it is hard for someone new to Perl to know what is going on in the trs80_way. Second it is slower, although it won't make a hill of beans difference if you only work with small hashes.

      That said, I still think the trs80_way looks cooler :^)


      Reference data:
      Benchmark script:
      use Benchmark; $hash1 = { a => '56', b => '75', c => '87' }; $hash2 = { d => '45', f => '46', g => '67', }; timethese(1000000, { trs80_way => sub { $hash1 = { %$hash1, %$hash2 }; }, tilly_way => sub { @$hash1{keys %$hash2} = values %$hash2; + }, } );

      Benchmark module documentation (this is a little out of date)

      I vaguly remember this in either TPJ or the Perl Cookbook, anyone else remember?