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

Dear perl -gurus, i am new to perl, it's been so much fun with perl but i hit a roadblock trying to make my programs a little more advanced. This example from the Cookbook has left me confused for two days and giving me a headache.

What is going on in the line

push (@{$ttys{$user}}, $tty );

I just cant get my head around, what is going on there ..does it fit a hash-key inside of an array index?.. I also dont understand why you would ever do that. I must have a fundamental misunderstanding going. Sorry .. Can anyone help me?

my %ttys = (); open(WHO, "who|"); while (<WHO>) { ($user, $tty) = split; push (@{$ttys{$user}}, $tty ); <--- WHAT ??? } foreach $user (sort keys %ttys) { print "$user: @{$ttys{$user}}\n"; }

Replies are listed 'Best First'.
Re: Help wanted .. cant get my head around array of hash
by moritz (Cardinal) on Feb 25, 2010 at 09:27 UTC

    Please put code here inside <code>...</code> tags, that way it's easier to read for us.

    push (@{$ttys{$user}}, $tty );

    There is a hash called %ttys. This statement access the item of this hash with the key $user, dereferences it as an array (that's the @{ ... } part) and pushes the variable $tty onto this array.

    You need to do such a thing if you have multiple key/value pairs with the same key.

    A user can have allocated several terminals (ttys), so for a mapping from user to terminal you need to account for the possibility of having more than one terminal per user.

    See also perlreftut and perldsc.

    Perl 6 - links to (nearly) everything that is Perl 6.
Re: Help wanted .. cant get my head around array of hash
by planetscape (Chancellor) on Feb 25, 2010 at 10:14 UTC
Re: Help wanted .. cant get my head around array of hash
by cdarke (Prior) on Feb 25, 2010 at 10:43 UTC
    I also dont understand why you would ever do that

    This kind of structure (described by others) can be considered a little advanced, so don't beat yourself up for not understanding it.

    Let's say we have a structure like this:
    my %Flintstones = (Stars => [qw(Fred Wilma)], Support => [qw(Barney Betty)]);
    Each key has a value of a reference to an array. If we print the value we just get the reference:
    print "$Flintstones{Stars}\n"; ARRAY(0x3a1bc)
    We obviously don't want that, we want it to be dereferenced as an array, which is where your syntax comes in:
    print "@{$Flintstones{Stars}}\n"; Fred Wilma
      Dear people, thank you so MUCH for the help. I just dont understand it still .. I think my mind may be unable to understand it. I have two possible explanations: OK, so when a hash item like $flintstones{stars} is "dereferenced as an array" it mean that the hash item is now treated in a "list context" producing a list with two items? Or 2) that the values stored at the reference (address) is treated as a list .. I think it would make better sense if the structure was defined like this to get an item from a hash with multiple values $flintstones{stars}[0]

        1) Try to write a program that does the following. Create a file with these lines in it:

        one hello two world three goodbye two apple one banana

        Now, collect all the words in the second column that have the same word in the first column. You want your program to display this(in any order):

        one: hello, banana two: world, apple three: goodbye

        Spend an hour trying to write that program.

        2) Next, read perlreftut. There is no point in discussing references unless you know the basics.

        3) A line such as this:

        push @{$hash_name{$key}}, $string;

        is magical. This part:

        $hash_name{$key}

        tells perl to retrieve the value from the hash that corresponds to the key. The @{ } around $hash_name{$key} tells perl to convert the value to an array--so you know the retrieved value has to be an array reference. Then push() adds $string to the array.

        Now, here is the magic part: if the key does not exist in the hash, normally $hash_name{$key} would return undef, and then you would be using @{ } to convert undef to an array, which doesn't work. However, perl magically creates a reference to an empty array for you, and subsequently @{ } converts the reference to an empty array. Then push adds $string to the empty array.

        In effect, the line:

        push @{$hash_name{$key}}, $string;

        tells perl to add $string to the array corresponding to $hash_name{$key}, but if the $key does not exist in the hash, create the key with a corresponding empty array, and add $string to the empty array.

        Here's the program:

        data1.txt:

        one hello two world three goodbye two apple one banana

        ========

        use strict; use warnings; use 5.010; open my $INFILE, '<', 'data1.txt' or die "Couldn't open file: $!"; my %hash; while (<$INFILE>) { chomp; my ($first_col, $second_col) = split; push @{$hash{$first_col}}, $second_col; } while ( my($first_col, $aref) = each %hash ) { say "$first_col: ", join(', ', @$aref); } close $INFILE; --output:-- three: goodbye one: hello, banana two: world, apple