perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I was looking for some minimal code to list out a hash -- stuff that could be 'in-lined', much like (join ", " @array)
can be thrown into a print statement to list out arrays.

My "simple"(sic?) test prog right now looks like:

#!/usr/bin/perl -w use strict; use 5.14.0; my %ahash=(one=>1, two=>2, three=>3); my $outp=[]; push @{$outp}, sprintf '"%s"=>"%s"', @$_ foreach @{&{ sub ($) { my $_=["",[]]; push @{$_->[1]},\@{$_->[0]} while @{$_->[0]=[each $_[0]]}; $_->[1] } } (\%ahash)}, @{$v}; printf "{%s}\n", join ", ", @{$outp};
What would like to do is find a way to embed the push statement into the printf -- making it similar to how arrays
are printable (am I missing an easier way mabye something using 'map'? Hmmm....

Anyway, nothing earth shaking is relying on this...just a some code quirkiness I was trying to deal with...

Why are array so much easier to print out than hashes...geez...

Replies are listed 'Best First'.
Re: A brain twister? (how to make 2 lines->1)
by muba (Priest) on Jun 25, 2012 at 02:08 UTC

    I assume this is along the lines of what you were looking for?

    use strict; use warnings; my %ahash=(one=>1, two=>2, three=>3, foo=>'$foo'); print join(", ", map { "'$_' => '$ahash{$_}'"} keys %ahash ), "\n"; __END__ 'three' => '3', 'one' => '1', 'foo' => '$foo', 'two' => '2'

    Why are array so much easier to print out than hashes...geez...
    I beg to differ. Just remember to read elements in pairs.
    use strict; use warnings; my %ahash=(one=>1, two=>2, three=>3); print join(", ", %ahash); __END__ three, 3, one, 1, two, 2

Re: A brain twister? (how to make 2 lines->1)
by roboticus (Chancellor) on Jun 25, 2012 at 02:14 UTC

    perl-diddler:

    If you don't have arrays & hashes in your hash, then a simple map might help:

    print "{", join(", ", map { "$_=>$ahash{$}" } keys %ahash), "}\n";

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      Absolutely what I was looking for yours and the one above -- I don't know what I was doing wrong when I tried it ...oh, I remember, I was trying to use map directly with the hash -- thinking it would treat it as an array and I could get through the keys/values that way..., but map only works on array according to the manpage.

      So then I started looking at the hash specific functions...

      Too bad 'each' doesn't do what keys/values does -- I tried

      @array=each %hash;, #and @array= [each %hash];...
      but never got it to do what I wanted...sigh.

      I've used map with values before, but was thinking there should be some way to do similar with 'each'...but each is just demented...1 item at a time...w/no slurp-ability......

      And there I am with a straw trying to make it work...

        but map only works on array according to the manpage
        No, map works on lists.
        but each is just demented...1 item at a time...w/no slurp-ability
        No, each fills a specific, useful niche. You are forgetting that a hash in list context returns its keys and values:
        @array=%hash;

        Dave.

        How about

        while (my ($key, $value) = each %hash) {...}

        See each for details. The example is directly from the documentation.

        --MidLifeXis

Re: A brain twister? (how to make 2 lines->1)
by AnomalousMonk (Archbishop) on Jun 25, 2012 at 03:51 UTC

    Even with nested arrays/hashes (see Data::Dump; why strain yer brain?):

    >perl -wMstrict -le "use Data::Dump qw(pp); ;; my %hash = (one => 1, two => 2, foo => [ 99, { tres => 3 } ]); ;; printf qq{%s \n}, pp \%hash; " { foo => [99, { tres => 3 }], one => 1, two => 2 }
Re: A brain twister? (how to make 2 lines->1)
by tobyink (Canon) on Jun 25, 2012 at 06:43 UTC
    use 5.014; use Data::Dumper; sub announce { say Data::Dumper->new(\@_)->Indent(0)->Sortkeys(0)->Terse(1)->Dump; } my %ahash = (one=>1, two=>2, three=>3); announce \%ahash;
    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: A brain twister? (how to make 2 lines->1)
by kcott (Archbishop) on Jun 25, 2012 at 07:04 UTC

    You can use the @{[ ... ]} construct.

    $ perl -Mstrict -Mwarnings -E 'my %y = qw{c 3 d 4}; say "@{[%y]}";' c 3 d 4

    The expression can be arbitrarily complex.

    $ perl -Mstrict -Mwarnings -E 'my %y = qw{c 3 d 4}; > say "@{[join q{;} => map { $_ . q{=} . $y{$_} } sort { $b cmp $a } k +eys %y]}";' d=4;c=3

    You can use a subroutine.

    $ perl -Mstrict -Mwarnings -E 'sub z { reverse @_ } my %y = qw{c 3 d 4 +}; > say "@{[z(%y)]}";' 4 d 3 c

    -- Ken

      Well, not that I need the whole problem solved, but it was important to me to be able to print the hashes in hash format -- meaning they'll be bracketed with {} and have => between key/values, a strictly comma separated list defeats the purposes.

      maybe using an embedded foreach w/each, since it seems like most of the above solutions lose the *visual* pairing.

      (I also print out arrays in x, y, z form, and just wanted my hashes to look hashy!...

      then again maybe just looping through using map & keys might be sufficient...only 400 ways to do this, just finding the 'right' one...that generates the correct output... ;-)

Re: A brain twister? (how to make 2 lines->1)
by MidLifeXis (Monsignor) on Jun 25, 2012 at 13:29 UTC

    When I find myself being too clever (or find that I have been too clever in a previous version of code), I try to simplify it. If I don't, I find myself spending way too much time trying to understand the clever code I wrote yesterday (or last week / month / year), and wanting to go back and smack some sense into myself.

    push @{$outp}, sprintf '"%s"=>"%s"', @$_ foreach @{&{ sub ($) { my $_=["",[]]; push @{$_->[1]},\@{$_->[0]} while @{$_->[0]=[each $_[0]]}; $_->[1] } } (\%ahash)}, @{$v};

    There is a lot packed into that code example that could probably be made more understandable, at the very least, with some whitespace.

    push @{$outp}, sprintf '"%s"=>"%s"', @$_ foreach @{&{ sub ($) { my $_=["",[]]; push @{$_->[1]}, \@{$_->[0]} while @{$_->[0]=[each $_[0]]}; $_->[1] } } (\%ahash)}, @{$v};

    although, in this case, I would probably give it some more care and feeding than just whitespace. Specifically (assuming that I am understanding what you are attempting to do), I would look at using map (map { sprintf... } along with keys instead of the foreach structure that you have.

    Assuming that I am understanding your intent (which I still may not have grokked), does this do what you are attempting?

    push @{$outp}, ( map { sprintf '"%s"=>"%s"', $_, $ahash{$_}; } keys $ahash, ), @{$v};

    Just my $0.02

    --MidLifeXis

      Your code generates correct output! (minus the @{$v} -- not sure how that got there...doesn't make any sense).

      And that may be the best solution I'm gonna be able to come up with for now..., but it isn't near as simple as 'join' for arrays... though I'm all but certain that the map is a better choice than the while loop.

      I was really wanting 'each' in place of keys, and in a list of lists context, it would generate something along the lines of ((k, v),(k2,v2)...), that I could then feed to map, and map would pull off one pair/ usage.

      Not saying it is possible, but for the work of the 'keys', an each doing the same would be of the same order as having an each that 'streams' kv pairs. -- I wanted to avoid the 2nd hash lookup ($ahash{$_}), as that makes the algorithm more like O(n^1.5), where keys and a streaming each would be of O(n).

      The 'keys' has already done the work of looping through the entire hash and thrown away the values part - which is regenerated by the $ahash{$_}... and was just trying to find a way to avoid that waste if it was possible.... a bit "arcane", but....it was a hope....

      I think my original solution might be closer to O(1), but the overhead of the while v. map would drown out any benefits even if it could be coerced into the right output...(order independent)...

      {"three"=>"3", "one"=>"1", "two"=>"2"}

        Hash lookups, if done right, are O(1) algorithms (see hash_table - thanks Ransom and possibly perldata (unverified) - thanks moritz). Since you are just looping through the list of keys (O(n)), looking up the hash table entry for that key (O(1)), and generating a string from the two values (O(1)), the order of the map should be O(n)*.

        Performance is another issue entirely, but unless you are profiling the two implementations, and the speed performance boost is appreciable and worth the additional complexity, go with the implementation that is simpler to understand.

        * - assuming that I am remembering my undergrad coursework properly.

        Update: used performance instead of speed since you may not be optimizing for speed, but some other metric.

        --MidLifeXis