1
{ 'a' =>1, % { sub() } }

Is this best for including a hash returned as ref into other?

%$sub() does not work.

As far I new {} makes new hash and returns reference, probably new optimized away this case, but not sure.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
jkl
  • 89
  • 8

2 Answers2

5

I'm guessing that you have some subroutine that returns a hash reference, and that you want to include all of those keys in a larger hash:

my $hash_ref = some_sub(...);
my $big_hash = { a => 1 };

There are a couple of ways that you can do. In you question, it looks like you are trying to do it inline by dereferencing the return value. That can be reasonable. The circumfix notation or postfix dereference can do this:

# general circumfix (old way)
my $big_hash = { a => 1, %{ some_sub() } };

# postfix dereference (new v5.24 hotness)
my $big_hash = { a => 1, some_sub()->%* };

However, I tend to not like to do this so directly. Typically I'm doing this when there are default values that might be overridden by whatever some_sub() is:

my %defaults = ( ... );

my %big_hash = ( %defaults, some_sub()->%* );

But, I usually go a step further by making the thing that produces the defaults into another subroutine so I can give subclassers a way to override it:

sub defaults {
   my %defaults = ( ... );
   return \%defaults;
   }

my %big_hash = ( defaults()->%*, some_sub()->%* );

There are many other ways to merge hashes. There's How can I combine hashes in Perl? on StackOverflow, but also How do I merge two hashes in perlfaq4.

But, there's another thing to consider. Simply mashing two hashes together to get a new one might be expensive. What if the first hash is very, very large?

my %grand_hash = ( %very_big_hash, %new_hash );

People often do this and assign back to the starting hash, mostly because it's easy to type:

my %grand_hash = ( %grand_hash, %new_hash );

You're telling Perl to unroll %grand_hash, combine another list with it, then re-hash the huge list.

Although a bit more unwieldy, a better way is to add the new keys and values.

foreach my $new_key ( keys %new_hash ) {
    $grand_hash{$new_key} = $new_hash{$new_key};
    }

That's nice when you need to do something else, such as skipping keys that you already have in the hash:

foreach my $new_key ( keys %new_hash ) {
    next if exists $grand_hash{$new_key};
    $grand_hash{$new_key} = $new_hash{$new_key};
    }

or maybe adding to a value that is already there instead of replacing it:

foreach my $new_key ( keys %new_hash ) {
    $grand_hash{$new_key} += $new_hash{$new_key};
    }

If you just need to add it and don't care about replacing values, a hash slice is nice for assigning multiple keys and values at the same time:

@grand_hash{ keys %new_hash } = values %new_hash;

In your case, you'd call the subroutine once and store the result so you don't have to construct the hash again. You then dereference that hash is the slice (or wherever you want to use it:

my $new_hash = some_sub(...);
@grand_hash{ keys %$new_hash } = values %$new_hash;
brian d foy
  • 129,424
  • 31
  • 207
  • 592
  • Cool that you provide so descriptive answer. It is like [Erwin](https://stackoverflow.com/a/7630564/4632019) answers. Such answers helpful and I hope will bring new blood (more newbies) into perl comunity – Eugen Konkov Mar 23 '20 at 14:36
3

Yes, to dereference a hash reference and get a list of key/value pairs, wrap %{ ... } around the expression that generates the hash reference.

Or, since perl 5.24, you can use the postfix dereference syntax sub()->%*

ysth
  • 96,171
  • 6
  • 121
  • 214
  • 1
    Thank you, postfix doesn't look much shorter or clearer. – jkl Mar 18 '20 at 23:41
  • 2
    For simple structures, the postfix doesn't shine. It's when you get into a long chain of things there reading strictly left to right really helps. – brian d foy Mar 19 '20 at 00:32