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

Hello everyone,

I have a little problem while trying to generate a hashtable with a map : I have something like
my %hash = map { (split /:/, $_ )[0] => (split /:/, $_ )[2] } <FILE>;
to generate my hashtable.
The problem is, when I want to display the keys of my hashtable :
print join ( "\n", sort keys %hash );
I only get the values : the keys are the result of
(split /:/, $_ )[2]
instead of the result of
(split /:/, $_ )[0]
If I try to display the values with these keys, I get the "real" keys ...

It's as if the keys were the values, and the values were the keys, and I have no idea why.

I thought for some time that $_ may be modified, either by the map, or by the split, but it doesn't seem to be the case.

I also thought that the map function may misunderstand the use of =>, thinking it could be seen as a comparison operator, or something.

I'm asking this question by curiosity, since I won't be generating my hashtable with the two splits, but I thought it was weird for this bit of code not to work.

I hope my question was clear enough, thanks for your answers :)

Tomtom

Replies are listed 'Best First'.
Re: Problem generating a hashtable
by davidrw (Prior) on May 13, 2005 at 13:23 UTC
    This (basically exact same as yours) code works for me (prints "k1\nk2\n") -- what is a sample of your <FILE>?:
    use strict; use warnings; my %hash = map { (split /:/, $_ )[0] => (split /:/, $_ )[2] } <DATA>; print join ( "\n", sort keys %hash ) . "\n"; __DATA__ k1:asd:v1 k2:asd:v2
    One suggestion though, would be to change the map so that the split() is only done once (no functionality change, just for efficiency):
    map { my @tmp = split /:/, $_; $tmp[0] => $tmp[2]; } <FILE>
    (could also do map { (split(/:/, $_)) [0,2] } but that's not as clear that it's key/value pairs)
    Update: Oops. I see now that OP said he won't be using the two splits....
      I only wish everyone who used map in their code would do so as beautifully as you did in your 2nd example. Multiple lines! Named variables! I can understand at a glance exactly what this does. Map has been a learning hurdle for me in all my years of Perl Development, mostly because nearly all examples of it I see obfuscate its actual function. Thank you for this good example.
      Actually, I changed it to
      my ( $key, $value ) = map { (split(/:/, $_)) [0,2] } <FILE>;
      :) In fact,
      (split /:/, $_ )[0] => (split /:/, $_ )[2]
      does work a first time on another tab, but seems to invert the keys and the values the second time :/
      could also do map { (split(/:/, $_)) [0,2] } but that's not as clear that it's key/value pairs
      This is indeed a good point, but if you're not too much concerned about it, then
      my %hash=map +(split /:/)[0,2], <DATA>;
      would be shorter and clearer, IMHO. In particular a very nice point about $_ is that it is the "topicalizer" so that it's the default implicit argument of just so many "actions"...
Re: Problem generating a hashtable
by rev_1318 (Chaplain) on May 13, 2005 at 13:26 UTC
    It works OK for me (perl 5.8.5 on Linux). Are you sure you're presenting the actual code?

    Also, you can simplify the map as follows:

    my %hash = map { (split /:/)[0,2] } <FILE>;

    Paul

Re: Problem generating a hashtable
by mrborisguy (Hermit) on May 13, 2005 at 13:26 UTC
    i don't have a reply to your question directly, but wouldn't only one split work better?
    my %hash = map { my ( $one, $two ) = (split /:/, $_ )[0,2]; $one => $two; } <FILE>;
    i didn't test this... so i can't be sure. it just seems more efficient to only split once. you can also test the values of $one and $two to see if you can find the problem.

    Update: yep, i guess he did say he won't actually be using two splits. my bad... i take it all back!
Re: Problem generating a hashtable
by holli (Abbot) on May 13, 2005 at 13:28 UTC
    the code below doesn't prove your statement. It works as expected on my system (perl 5.8, win xp). what's your perl?
    use strict; my %hash = map { (split /:/, $_ )[0] => (split /:/, $_ )[2] } <DATA>; print join ",", sort keys %hash; #key1,key2 __DATA__ key1:useless:value1 key2:useless:value2

    Update:
    Darn, too slow again.


    holli, /regexed monk/
      The version of Perl I'm using is ... 5.005 :/
      I'd rather work on a 5.8, it you asked me, but I don't have the choice :(
        What's the output when you run my code above?


        holli, /regexed monk/