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

Hello brethren - I am just now learning perl and how to use hashes. I have written the following script:
#!/usr/bin/perl -w use strict; my %email_owners; my @owners; my @addys; %email_owners = ('mike@foo.net' => 'Mike Foo', 'jwtheis@smic.net' => 'Jim Theis', 'happy@bobbarker.net' => 'Happy Gilmore'); @owners = values (%email_owners); @addys = keys (%email_owners); foreach (@owners) { print "Owner: $_\n"; foreach (@addys) { print "E-mail: $_\n"; } }
What I am trying to accomplish is I want the output to be Owner: value then E-mail: key for each pair one by one. However when I run this script, it returns the Owner properly, but then it lists out the three keys that I have listed. I have reviewed my books and I'm not sure where I went wrong, can anyone help, please? Thanks much!

Replies are listed 'Best First'.
Re: First time using hashes
by broquaint (Abbot) on Apr 01, 2003 at 14:50 UTC
    How about
    %email_owners = ( 'mike@foo.net' => 'Mike Foo', 'jwtheis@smic.net' => 'Jim Theis', 'happy@bobbarker.net' => 'Happy Gilmore' ); while(my($addy, $owner) = each %email_owners) { print "Owner: $owner\n"; print "E-mail: $addy\n"; } __output__ E-mail: jwtheis@smic.net Owner: Happy Gilmore E-mail: happy@bobbarker.net Owner: Mike Foo E-mail: mike@foo.net
    Or simpler still
    print "Owner: $email_owners{$_}\nE-mail: $_\n" for keys %email_owners;
    See. perldsc for more info on data structures in perl.
    HTH

    _________
    broquaint

Re: First time using hashes
by jasonk (Parson) on Apr 01, 2003 at 14:52 UTC
    #!/usr/bin/perl -w use strict; my %email_owners = ('mike@foo.net' => 'Mike Foo', 'jwtheis@smic.net' => 'Jim Theis', 'happy@bobbarker.net' => 'Happy Gilmore'); foreach (keys %email_owners) { print "Owner: $email_owners{$_}\n"; print "E-mail: $_\n"; }

    Because of the way you have the loop setup, you will go through the @addys array completely for every element in @owners, so by having three elements in @owners and three in @addys, you end up printing 9 emails. By splitting the hash into two arrays, you ended up disassociating the keys from the values.

    If you really wanted to do it with two arrays, you could use something like this:

    for(0 .. $#owners) { print "Owner: $owners[$_]\n"; print "E-mail: $addys[$_]\n"; }

    But then you really defeat the whole purpose of the hash.


    We're not surrounded, we're in a target-rich environment!
Re: First time using hashes
by Aragorn (Curate) on Apr 01, 2003 at 14:58 UTC
    You can use the each function:
    #!/usr/bin/perl -w use strict; my %email_owners; %email_owners = ( 'mike@foo.net' => 'Mike Foo', 'jwtheis@smic.net' => 'Jim Theis', 'happy@bobbarker.net' => 'Happy Gilmore' ); my ($owner, $addy); while ( ($addy, $owner) = each %email_owners) { print "Owner: $owner\nEmail: $addy\n"; }

    Arjen

Re: First time using hashes
by Trimbach (Curate) on Apr 01, 2003 at 14:56 UTC
    Your hash is fine... your output loop is not. You're telling Perl to "print one owner, then print all three addys, then print one owner, then print all three addys" etc. This is what happens when you nest one loop inside of another.

    In general most of the time you don't need to break down the keys and values in a hash into separate arrays. If you want to list each owner and their address you might do this instead:

    foreach my $addy(keys %email_owners) { print "Owner: $email_owners{$addy}\n"; print "Address: $addy\n\n"; }
    ...and that's it. You also might consider renaming your hash so that it better describes your key. In your case, the hash is keyed by email address, not email owner. So, renaming %email_owners to something like %email_addresses makes the output loop syntax more "natural":
    foreach my $addy(keys %email_addresses) { print "Owner: $email_addresses{$addy}\n"; print "Address: $addy\n\n"; }

    Gary Blackburn
    Trained Killer

Re: First time using hashes
by IPstacks (Novice) on Apr 01, 2003 at 14:53 UTC
    Thanks a lot! I will read that and study further. -ipstacks