Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Hash value printing... WITH ARRAYS *dun dun dun*

by jaydstein (Novice)
on Feb 06, 2012 at 22:33 UTC ( [id://952178]=perlquestion: print w/replies, xml ) Need Help??

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

I'm relatively new to perl and I'm at the point where I don't 100% understand the nitty gritty of what I am doing (but I'm trying ;) ). I'm not getting expected behavior from my code. I have a hash that contains either a scalar or an array, and I need to print, line-by-line, each value contained in a hash.

Here's my code:

my $primaryFeatures = { 'foo', ('fool', 'food', 'foot'), 'bar', ('barricade'), }; while (my ($key, $value) = each(%$primaryFeatures)){ print "($key, $value)\n"; }

Here's what I want:
(foo, fool)
(foo, food)
(foo, foot)
(bar, barricade)

But one of the items in the array value ('fool', 'food', 'foot') is being picked up and interpreted as a key instead of a value. Here is what I am getting:
(foo, fool)
**(food, foot)**
(bar, barricade)

I've looked into the 'values' function. I could make that work... but sifting through the the keys and values for only the values is a little messy. What is the best way to do this?

Replies are listed 'Best First'.
Re: Hash value printing... WITH ARRAYS *dun dun dun*
by toolic (Bishop) on Feb 06, 2012 at 22:49 UTC
    I think you really want a hash-of-arrays (see perldoc perldsc):
    use warnings; use strict; my $primaryFeatures = { 'foo', ['fool', 'food', 'foot'], 'bar', ['barricade'], }; while (my ($key, $value) = each(%$primaryFeatures)){ print "($key, $_)\n" for @{ $value }; } __END__ (bar, barricade) (foo, fool) (foo, food) (foo, foot)

    I used square brackets to construct the arrays, then I dereferenced the arrays in the while loop.

    It is also customary to use fat commas (=>) when constructing hashes

    my $primaryFeatures = { foo => ['fool', 'food', 'foot'], bar => ['barricade'], };
Re: Hash value printing... WITH ARRAYS *dun dun dun*
by kcott (Archbishop) on Feb 06, 2012 at 23:46 UTC

    The format of a hash is basically (key, value, key, value, etc.) so, despite adding the parentheses, what you are doing here is mapping foo to fool, food to foot and bar to barricade. An arrayref (reference to an array) is a single value and that's what you should be using here. So, your code becomes:

    my $primaryFeatures = { 'foo', ['fool', 'food', 'foot'], 'bar', ['barricade'], };

    You'll need to dereference the arrayref (i.e. @$value) and print each element in some sort of loop. Simplistically, change the print statement to something like:

    for my $element (@$value) { print "($key, $element)\n"; }

    That should get the code doing what you want. In addition, here's a few other suggestions:

    • You can remove much of the punctuation-noise by using => (called the fat comma) and qw{} (quote-on-whitespace): foo => [qw{fool food foot}]
    • Unless you really need a hashref, just use a hash: my %primaryFeatures = (...); and later each(%primaryFeatures)
    • While there are many ways to do this, while and each are probably not the best choices here (I think you've probably guessed as much already) - for and keys would be better.
    • Add use strict; and use warnings; to the top of your code.

    Putting all that together, your code might look like:

    use strict; use warnings; my %primaryFeatures = ( foo => [qw{fool food foot}], bar => [qw{barricade}], ); for my $key (keys %primaryFeatures) { for my $element (@{$primaryFeatures{$key}}) { print "($key, $element)\n"; } }

    each(), keys() and values() do not guarantee the order of data returned. If you want the exact order you've shown above, you'll need additional code. On my machine, this code outputs:

    (bar, barricade) (foo, fool) (foo, food) (foo, foot)

    Finally, what I have here is not so much the correct answer but rather one of many possible working solutions. On this site and in Perl books and documentation you'll often come across the Perl motto: TMTOWTDI (There's more than one way to do it).

    -- Ken

Re: Hash value printing... WITH ARRAYS *dun dun dun*
by Anonymous Monk on Feb 06, 2012 at 22:56 UTC
    Your data structure doesn't look like you think it does. Values can't be arrays. They have to be references. Here's some similar code that may help you get going better. I also threw in a little use of Data::Dumper since it can be handy to figure out what what your data structures look like.
    use warnings; use strict; use Data::Dumper; my $primary_features = { foo => [ 'fool', 'food', 'foot', ], bar => [ 'barricade' ] }; print Data::Dumper::Dumper( $primary_features ); while (my ($key, $value) = each(%$primary_features)){ print "($key, $value)\n"; }
    Also, note a few things:
    • I used => instead of ",". They're almost the same, except => puts whatever is to the left of it in quotes, and it's traditionally used to separate the keys and values of things in hash definitions.
    • I made a reference to an array with square brackets instead of parentheses. You can make an array with the qw() operator, so qw( fool food fort ) is the same as 'fool', 'food', 'fort' , and may be easier to type.
    • When you print out $value, it's going to show something like "ARRAY(0x10080fe18))" that means you're trying to print an array. You can iterate over your array or something like that to print something more useful.
    • use warnings and strict. They help you find when you're typing something that doesn't make sense.
Re: Hash value printing... WITH ARRAYS *dun dun dun*
by mcdave (Beadle) on Feb 07, 2012 at 04:03 UTC
    Lots of folks gave reasonable explanations of what the right answer might look like, but in case you're wondering what was wrong with the original: The "values" for foo and bar actually got flattened out into the list itself. So what you wrote was equivalent to
    my $primaryFeatures = { 'foo', 'fool', 'food', 'foot', 'bar', 'barricade', };
    which is equivalent to
    my $primaryFeatures = { foo => 'fool', food => 'foot', bar => 'barricade', };
    which is why the (food, foot) pairing came out.
Re: Hash value printing... WITH ARRAYS *dun dun dun*
by InfiniteSilence (Curate) on Feb 07, 2012 at 00:34 UTC
    Another way entirely:
    ~linux> perl -MMath::Cartesian::Product -e 'my %hash = (q|foo|=>[qw|fo +ol food foot|],q|bar|=>[qw|barricade|]); for (keys %hash) {cartesian +{print qq|@_\n|} [$_],$hash{$_}};'
    Produces
    bar barricade foo fool foo food foo foot
    TIMTOWTDI

    Celebrate Intellectual Diversity

Re: Hash value printing... WITH ARRAYS *dun dun dun*
by JavaFan (Canon) on Feb 07, 2012 at 00:32 UTC
    Untested code:
    use 5.010; my $hash = { array_key => ['one', 'two', 'three'], scalar_key => 'four', }; while (my ($key, $value) = each %$hash) { foreach my $element ("ARRAY" eq ref $value ? @$value : $value) { say "($key, $element)" } }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://952178]
Approved by toolic
Front-paged by MidLifeXis
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (2)
As of 2024-04-20 03:18 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found