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

how can I push onto an anonymous array which is a value in a hash without creating it first?

e.g.

for (@somelist) { my($foo,$bar) = split; my $x = somecalc($foo,$bar); # this line won't work!! push @$somehash{$foo.$bar}, $x; }
problem is I don't know what $foo and $bar are so I can't create the anonymous list before I get into the loop and if I did $somehash{$foo.$bar} = []; first then surely it would reset the list each time round the loop!?

please help.

Replies are listed 'Best First'.
Re: push onto hash!
by lachoy (Parson) on Apr 03, 2001 at 20:07 UTC

    You don't normally need to initialize (autovivification will take care of this) but you can use the ||= construction:

    for (@somelist) { my($foo,$bar) = split; my $x = somecalc($foo,$bar); $somehash{ $foo }->{ $bar } ||= []; push @{ $somehash{ $foo }->{ $bar } }, $x; }

    ||= basically says 'if the left-hand value isn't defined, assign the right-hand side to it'.

    Chris
    M-x auto-bs-mode

Re: push onto hash!
by Tortue (Scribe) on Apr 03, 2001 at 20:17 UTC
    # this line won't work!! push @$somehash{$foo.$bar}, $x;
    This one will work better :     push @{$somehash{$foo.$bar}}, $x;
Re: push onto hash!
by arturo (Vicar) on Apr 03, 2001 at 20:05 UTC

    use perlfunc:ref to see if the value of $somehash{"$foo$bar"} is an arrayref. If it isn't, then create it.

    unless ( ref ($somehash{"$foo$bar"} eq 'ARRAY' ) { $somehash{"$foo$bar"} = []; } ...

    HTH

    Philosophy can be made out of anything. Or less -- Jerry A. Fodor

      As noted later, your use of ref is not necessary because you can autovivify the hash entry and push onto it in one step:

      push @{$somehash{"$foo$bar"}}, $x;

      However, another point is to reconsider ever using ref. As was made clear to me this node, most uses of ref indicate that the code design should be revisited. ref is typically used to handle "special cases" and make things magically "work", which typically suggests a design flaw. Go to that link and read it. Good stuff! Once I grokked it, I rewrote the main loop in the code that I was making my point with and discovered that my new loop, without using ref, was smaller and easier to maintain.

      Cheers,
      Ovid

      Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

(sacked: hash slice notation) Re: push onto hash!
by sacked (Hermit) on Apr 05, 2001 at 02:14 UTC
    I just wanted to follow up and explain _why_ your code doesn't work the way you expect and why it is necessary to use the notation shown by Tortue, Ovid, and lachoy above.
    push @$somehash{$foo.$bar}, $x; # is the same as push @{$somehash}{$foo.$bar}, $x;
    Perl sees $somehash as a scalar holding a reference to a hash. Paraphrasing from the Panther, when parsing an expression like this, key lookups are done last. Since the hashref is dereferenced before the key lookups are done, the @ is applied to return a hash slice.

    (aside: With a regular hash %somehash, a one-element hash slice (which is usually not what you want) would look like @somehash{$foo}, but with a scalar $somehash as a hashref, you would use @$somehash{$foo} or @{$somehash}{$foo} as above.)

    But you didn't want a hash slice-- you wanted to dereference an array reference. In order to do that, you put braces around the scalar holding the array ref. Now we have @{ $somehash{$foo.$bar} }, which is the notation shown by the other monks earlier in this thread. The outer braces force the hash lookup to be done before the @ is applied to dereference the returned value (which, of course, is an array ref). I hope this helps explain what was happening in your code. Also, for a good reference on, well, references, check tye's excellent references quick reference (Re: push).

    --sacked