in reply to Hash assignments using map

Your map statement is saying "take the elements of @keys and return a list consisting of each element, incremented." When you assign a list to a hash, Perl expects an even-numbered list of key-value pairs.

Replies are listed 'Best First'.
Re^2: Hash assignments using map
by njcodewarrior (Pilgrim) on Feb 24, 2007 at 16:17 UTC

    Hi friedo

    I'm still not clear on this. Isn't that what I'm doing in here:

    my %hash; my @keys = qw{ a b c d }; foreach ( @keys ) { $hash{$_}++; }

    Can you clarify for me?

    thanks - njcodewarrior

      njcodewarrior,
      You're right in that the map function acts like a loop. Where is gets tricky is when you assign the output of the map function to a hash, because the hash assignment operation evaluates two elements at a time. So Perl does the equivalent of this:
      my $i=0; while ($i<$#group) { $hash{$group[$i]}=$group[$i+1]; $i+=2; }
      As other monkers already pointed out the "odd number of elements" warning will be generated when you feed the hash with a group that has an odd number of elements.

      Now if we look at the map function itself in your code

      @destination = map {$_++} @source
      The effect of the above code is that the elements are copied one by one. At the same time the element in the source (!) group is changed/incremented. Thatīs probably not what you wanted, right?

      If the destination of the map function is a hash then commonly the map is used to produce 2 elements at a time in a list fashion:

      .... = map { ("key$_" , "value$_") } .....
      For clarity purpose the comma operator is typically replaced by a '=>' operator to indicate that we mean to produce something for a hash.
      .... = map { ("key$_" => "value$_") } .....
      I'm not sure what you hoped to achieve through your code but hopefully this shows why your code behaved as it did

        Thanks varian. This makes sense.

        I want to quickly create a hash with keys that are the elements of the list. All I want access to is the keys...I don't care what the values are. I can then use exists to see if those keys either do or do not exist in another hash.

        Thanks again!

      the first statement takes the elements from the array @keys, increments them and adds to the hash %hash.
      first point is, you only have letters, no numbers, so Perl won't "increment" them, and will just add them as they already are to the hash.
      secont point, the hash takes two elements to populate a key-value pair. for example, a hash %h = (a => 1, b => 2, c => 3) could also be written as %h = qw{a 1 b 2 c 3}.
      the second statement takes each element from the array as a key of the hash, incrementing its value each time it's processed (which means if you have twice the element "a" in the array, the value of the key "a" in the hash will be 2 etc). if you want to use map to do that, it'd like map { $hash{$_}++ } @keys.

      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      *women.pm
        first point is, you only have letters, no numbers, so Perl won't "increment" them

        Really?

        $ perl -le '$c = "a"; print ++$c' b

        Check perlop:

        The auto-increment operator has a little extra builtin magic to it. ... If, however, the variable has been used in only string contexts since it was set, and has a value that is not the empty string and matches the pattern "/^[a-zA-Z]*[0-9]*\z/", the increment is done as a string, preserving each character within its range, with carry ...

        Update: Though you're actually right, but only by coincidence. Using the postfix autoincrement won't make the hash elements incremented.

        $ perl -Mstrict -MData::Dumper -we 'my @a = qw(a b c d); \ my %h = map { $_++ } @a; print Dumper \%h' $VAR1 = { 'c' => 'd', 'a' => 'b' };


        --chargrill
        s**lil*; $*=join'',sort split q**; s;.*;grr; &&s+(.(.)).+$2$1+; $; = qq-$_-;s,.*,ahc,;$,.=chop for split q,,,reverse;print for($,,$;,$*,$/)