Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Generating Hash-of-List using map?

by cmv (Chaplain)
on Sep 23, 2013 at 16:19 UTC ( [id://1055327]=perlquestion: print w/replies, xml ) Need Help??

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

Hi Monks-

I don't quite understand why this one is so hard for me to figure out - please help!

This code:

use warnings; use strict; use Data::Dumper; my @CELLS=( 'A-1', 'A-2', 'A-3', 'A-4', 'B-5', 'B-6', 'C-7', 'C-8'); my %hash=map { /^(.*)-(\d+)$/; $1=>$2} @CELLS; print STDERR "hash DUMPER:\n", Dumper(\%hash), "\n";
Generates a Hash whose key is the alphabetical letter found before the dash, and whose value is the last numerical digit found for that letter
hash DUMPER: $VAR1 = { 'A' => '4', 'C' => '8', 'B' => '6' };
How can I change the map command so that it will generate a list of all the numerical digits found? Something like this:
hash DUMPER: $VAR1 = { 'A' => [ 1, 2, 3, 4 ], 'C' => [ 7, 8 ], 'B' => [ 5, 6 ] };

Thanks

-Craig

Replies are listed 'Best First'.
Re: Generating Hash-of-List using map?
by moritz (Cardinal) on Sep 23, 2013 at 16:29 UTC

    There are two approaches: either you can add state information to the block of the map, in which case writing it as an ordinary for-loop is probably much less confusing. Or you can do a separate reduction step which keeps the state for you.

    Perl 5 is well suited for the first approach, so here it is:

    use warnings; use strict; use Data::Dumper; my @CELLS=( 'A-1', 'A-2', 'A-3', 'A-4', 'B-5', 'B-6', 'C-7', 'C-8'); my %hash; for (@CELLS) { /^(.*)-(\d+)$/ and push @{ $hash{$1} }, $2 } print STDERR "hash DUMPER:\n", Dumper(\%hash), "\n";

    The second approach becomes much easier when you have objects for pairs, so it's easier to pass them around as a scalar. Here is a Perl 6 implementation:

    use v6; my @CELLS= 'A-1', 'A-2', 'A-3', 'A-4', 'B-5', 'B-6', 'C-7', 'C-8'; my %hash; %hash.push: @CELLS.map: *.split('-'); say %hash.perl;

    If you think that's cheating, because Hash.push does all the heavy work, here is a version which doesn't use it:

    use v6; my @CELLS= 'A-1', 'A-2', 'A-3', 'A-4', 'B-5', 'B-6', 'C-7', 'C-8'; my %hash = @CELLS.map({ my ($k, $v) = .split('-'); $k => $v})\ .categorize(*.key)\ # group by key .map({; .key => [.value>>.value]}) # list of pairs t +o list of values ; say %hash.perl;

    (Updated to add the Perl 6 stuff).

Re: Generating Hash-of-List using map?
by BrowserUk (Patriarch) on Sep 23, 2013 at 17:14 UTC

    One of the cases where for is just easier than map:

    my %hash; m[(.+)-(\d+)] and push @{ $hash{$1} }, $2 for @CELLS;; pp \%hash;; { A => [1, 2, 3, 4], B => [5, 6], C => [7, 8] }

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Generating Hash-of-List using map?
by LanX (Saint) on Sep 23, 2013 at 17:06 UTC
    probably not an expected answer:

    DB<117> %hash=() DB<118> map { ($k,$e)=split '-'; push @{ $hash{$k} },$e } @CELLS => (1, 2, 3, 4, 1, 2, 1, 2) DB<119> \%hash => { A => [1, 2, 3, 4], B => [5, 6], C => [7, 8] }

    update

    or

    DB<125> %hash=() DB<126> push @{ $hash{$_->[0]} } , $_->[1] for map {[split '-' ]} @ +CELLS => "" DB<127> \%hash => { A => [1, 2, 3, 4], B => [5, 6], C => [7, 8] }

    or

    DB<128> %hash=() DB<129> map { push @{ $hash{$_->[0]} } , $_->[1] } map {[split '-' +]} @CELLS => (1, 2, 3, 4, 1, 2, 1, 2) DB<130> \%hash => { A => [1, 2, 3, 4], B => [5, 6], C => [7, 8] }

    or ( but not recommended b/c of possible side effects)

    DB<135> %hash=() DB<136> map { @_=split '-'; push @{$hash{$_[0]}},$_[1] } @CELLS => (1, 2, 3, 4, 1, 2, 1, 2) DB<137> \%hash => { A => [1, 2, 3, 4], B => [5, 6], C => [7, 8] }

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Re: Generating Hash-of-List using map?
by hdb (Monsignor) on Sep 23, 2013 at 19:24 UTC

    All proposals are cheating so far! Here is what you are looking for (warning: dirty code!)

    my %hash = map { /^([^-]*)-(.*)$/; if( $1 eq ($a//'-') ) { push @$b, $ +2; () } else { ( $a = $1 => $b = [ $2 ] ) } } @CELLS;

    Of course, should $a accidentally be identical to the bit left of the hyphen in the first element of @CELLS, then it fails...

    UPDATE: Small improvement on the else branch.

      I have the suspicion that this only works with sorted input while the "cheating propsosals" always work...

      DB<148> %hash=() DB<149> @CELLS => ("A-1", "A-2", "A-3", "A-4", "B-5", "B-6", "C-7", "C-8", "A-9") DB<150> %hash = map { /^([^-]*)-(.*)$/; if( $1 eq ($a//'-') ) { push + @$b, $2; () } else { ( $a = $1 => $b = [ $2 ] ) } } @CELLS; => ("B", [5, 6], "C", [7, 8], "A", [9])

      oops, indeed! ;-)

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        Oh, #@%$£"!

        I admit defeat. A sort is probably not what the OP specified. One could build a full temporary hash in the map but that would be cheating as well (also relies on $a not been used for anything else before):

        my %hash = map { /^([^-]*)-(.*)$/; exists $$a{$1}? (push(@{$$a{$1}},$2 +) and ()): ( $1=>$$a{$1}=[$2] ) } @CELLS;

        So there is really no way around first declaring the hash and then filling it. What a pity!

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (3)
As of 2024-04-25 23:39 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found