Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses

from array to hash with grep

by jeanluca (Deacon)
on Jun 15, 2006 at 07:44 UTC ( #555439=perlquestion: print w/replies, xml ) Need Help??
jeanluca has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

once in a while I try map and grep, but normally I avoid them becaus they are so complex.
Anyway, this time I tried todo the following with grep:
#! /usr/bin/perl use strict ; use warnings ; my @inp = ("abc_A_bla", "abc_B_bla", "abc_C_bla") ; my %inp ; $inp{$_} = 1 grep ( /\w{3}_(\w)/, @inp ) ; foreach ( keys %inp ) { print "Found $_\n" ; }
So, I would like to create a hash with the keys A, B and C. Running this gives an error near grep.
Can someone tell me what I do wrong here with grep?


Replies are listed 'Best First'.
Re: from array to hash with grep
by bart (Canon) on Jun 15, 2006 at 08:04 UTC
    "You can't make shit up and expect Perl to know what it means, retardo!"... ((in)famous MJD quote)

    There must be a logical technical reason to how it should/could work. grep reads a list and checks for which ones the condition evaluates to true. That's all. That doesn't look very useful.

    Now you're try this with map, it'll build a list of return values instead. Now what would the value returned from the regexp be? Why, you have capturing parens, map calls the regexp in list context, so it'll return an empty list for no match, and a list of captured values for matches. So

    @result = map /\w{3}_(\w)/, @inp;
    will put ('A', 'B', 'C') into the array, for your particular input.

    If you foreach through that list, you can use each as a hash key, in turn. So this will do what you want:

    $inp{$_} = 1 foreach map /\w{3}_(\w)/, @inp;

    It looks close to what you made up, but it isn't. Not really.

      OK, al that makes me understand map and grep better. I just realized that I want it a little bit different, I need a hash with the same keys but the values should be the whole string, so you would get:
      $inp{A} = 'abc_A_bla' ; etc
      Is this still possible with map or should I switch to a simpel foreach
      foreach ( @inp ) { ($key) = $_ =~ /\w{3}_(\w)/ ; $inp{$key} = $_ ; }

        It's still possible but you'll have to use something closer to what sonofason wrote. For each match, you need to create a ($key, $value) pair so the list constructed by map is more like ($key1, $value1, $key2, $value2, $key2, $value3) for the three values. That way, assigning to the hash will insert them as you want.

        Now the code you can use to do that, can read for example:

        @flat = map { /\w{3}_(\w)/ ? ($1, $_) : () } @inp;
        It'll create a pair like ("A", "abc_A_bla") for a match, and an empty list for no match.

        Assign to the hash, and you get:

        %inp = map { /\w{3}_(\w)/ ? ($1, $_) : () } @inp;
        my @inp = ("abc_A_bla", "abc_B_bla", "abc_C_bla"); my %inp = map { /\w{3}_(\w)/; $1 => $_ } @inp ; foreach ( keys %inp ) { print "$_ => $inp{$_}\n"; }

        Update: Better to use bart's version which includes checks for when the regex doesn't match.


        "The first rule of Perl club is you do not talk about Perl club."
        -- Chip Salzenberg

Re: from array to hash with grep
by davorg (Chancellor) on Jun 15, 2006 at 08:22 UTC

    A quick rule of thumb which might help to decide if you want grep or map.

    If you have a list and you want to filter that list so that you don't get back all of the items, then you probably want grep.

    If you have a list and you want to transform each item in the list in some way then you probably want map.

    Generally speaking, given a list with N items, grep returns a list with <= N items and map returns a list with N items[1]

    [1] Yes, I know that map can actually return more than N items and even less than N items - but my explaination is true to a first approximation.


    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

Re: from array to hash with grep
by sonofason (Sexton) on Jun 15, 2006 at 08:00 UTC

    Try using map instead:

    my %inp = map {/^\w{3}_(\w)/; ($1, 1)} @inp;
Re: from array to hash with grep
by graq (Curate) on Jun 15, 2006 at 08:12 UTC

    I expect there are plenty of 'use map!' replies before I finish this, but I'll throw my torch light on the subject.

    $inp{$_} = 1 grep ( /\w{3}_(\w)/, @inp ) ;

    This is initially syntactically wrong. You have a list and need to iterate over it somehow.

    $inp{$_} = 1 for grep ( /\w{3}_(\w)/, @inp );

    However grep returns the line that matched, not what you 'captured' in $1, so you will match all lines with no change.

    Hence you need to use map.

    $inp{$_} = 1 for map ( /\w{3}_(\w)/, @inp );

    -=( Graq )=-
Re: from array to hash with grep
by ioannis (Monsignor) on Jun 15, 2006 at 08:19 UTC
    Since it is fixed-length scanning, we can use pack() and unpack():
    my @inp = ("abc_A_bla", "abc_B_bla", "abc_C_bla") ; print "@{[ keys %{{ unpack '(x4 A A4)*', pack( '(A*)*', @inp) }}]}";
Re: from array to hash with grep
by Moron (Curate) on Jun 15, 2006 at 08:49 UTC
    At first sight it looked as if a minor modification was necessary, but grep has run through the array and returned a list before the lhs can get at each match variable, preventing them being assigned to keys of %inp (normally you'd use $1 not $_).

    map was probably invented for this kind of reason. But the example doesn't really demand it that strongly when the lhs is a hash - 'for' still seems adequate and simple to me:

    #!/usr/bin/perl use strict ; use warnings ; my @inp = ("abc_A_bla", "abc_B_bla", "abc_C_bla") ; my %inp ; $inp{ $1 } = /\w{3}_(\w)/ || next for @inp; foreach ( keys %inp ) { print "Found $_\n" ; }


    Free your mind

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://555439]
Approved by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (7)
As of 2018-06-25 08:21 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (126 votes). Check out past polls.