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

Hello monks, I would like your help in understanding the use of map to do hash assignments, ive looked at the faq on here, the cookbook and the camel book but I cant quite work this out.. I have two strings in an array
my @teststr; my %testhash; $teststr[0] = "32977186 4 -rw-r--r-- 1 owner mygrp 65 Aug 4 +13:16 /long/path/to/my/file/file1.txt"; $teststr[1] = "32977186 4 -rw-r--r-- 1 owner mygrp 65 Aug 4 +13:16 /long/path/to/my/file/file2.txt";
and I want to put them in the hash to show
file1=>file1.txt file2=>file2.txt
it works fine if I use foreach:
foreach my $line (@teststr) { my ($key, $val); $val = (split /\//, $line)[-1]; ($key = $val) =~ s/\.txt//; $testhash{$key} = $val; }
but I think I should be able to use map to do this, I cant seem to account for the changing $_
my %testhash = map {(my $val = (split /\//, $_)[-1]); ((my $key = $va +l) =~ s/\.txt//);} @teststr;
gives
1 => 1
i think im getting the return value of the s//, so how can I do the split then run the regex on the result? thanks

Replies are listed 'Best First'.
Re: hash assignment using map with multiple statements
by ikegami (Patriarch) on Sep 19, 2008 at 08:27 UTC

    You forgot to specify the return value for map. It's currently returning the result of s/// (which is 1) since it's the last operation performed.

    my %testhash = map { my $val = (split /\//, $_)[-1]; (my $key = $val) =~ s/\.txt//; $key => $val # <-- added } @teststr;
      brilliant thanks, that was what was missing!!! now it works and im so happy ;+)
Re: hash assignment using map with multiple statements
by oko1 (Deacon) on Sep 19, 2008 at 13:48 UTC

    As ikegami has mentioned, you have to remember what "map" returns - as the docs say, it's an evaluation of each operation within the block as a list (in my experience, the "s///" inside a "map" is one of the most common mistakes that new users of "map" make.)

    Incidentally, since you're already using a regex inside your block, you don't really need that "split":

    #!/usr/bin/perl -w use strict; my @teststr = ( "32977186 4 -rw-r--r-- 1 owner mygrp 65 Aug 4 13:16 /long/pa +th/to/my/file/file1.txt", "32977186 4 -rw-r--r-- 1 owner mygrp 65 Aug 4 13:16 /long/pa +th/to/my/file/file2.txt", "32977186 4 -rw-r--r-- 1 owner mygrp 65 Aug 4 13:16 /long/pa +th/to/my/file/file3.txt", "32977186 4 -rw-r--r-- 1 owner mygrp 65 Aug 4 13:16 /long/pa +th/to/my/file/file4.txt", ); my %testhash = map { m{(([^/]+)\.[^/]+)$}; $2, $1 } @teststr; print "$_ => $testhash{$_}\n" for sort keys %testhash;

    Output:

    file1 => file1.txt file2 => file2.txt file3 => file3.txt file4 => file4.txt

    --
    "Language shapes the way we think, and determines what we can think about."
    -- B. L. Whorf
Re: hash assignment using map with multiple statements
by GrandFather (Saint) on Sep 19, 2008 at 21:14 UTC

    With just a little tidying the for loop version looks much better and is easier to understand (at least for me) than the map version. Consider:

    foreach (@teststr) { my $val = (split /\//)[-1]; $testhash{substr $val, 0, index $val, '.txt'} = $val; }

    Perl reduces RSI - it saves typing
Re: hash assignment using map with multiple statements
by repellent (Priest) on Sep 19, 2008 at 22:48 UTC
    For robustness and clarity, you ought to use well-defined modules instead of split and s///
    use Text::ParseWords; use File::Basename; my %testhash = map { my ($file, $dir, $suffix) = fileparse((shellwords $ +_)[-1], '\.txt'); $file => $file . $suffix } @teststr;
      thanks plenty to think about here, some background info, i am loading c5k filenames as an initialisation process of a larger app, so perhaps File::Basename is a better way to go currently I do this ( i cant  `ls -1` as ls borks with too many arguments error)
      my @teststr = `find $filearea -name "*.txt" -ls`;
      the current design probably isnt great, but Id be interested in hearing opinions on the relative speed of the different solutions: e.g. is a system call likely to be more expensive than the module? ill try and do some benchmarking myself thanks
Re: hash assignment using map with multiple statements
by lamp (Chaplain) on Sep 19, 2008 at 09:14 UTC
    And also like foreach, we can do hash assignments inside the map.
    map { my $val = (split /\//, $_)[-1]; (my $key = $val) =~ s/\.txt//; $testhash1{$key} = $val } @teststr;

      Why would you do that? Just about everyone who sees map expects that the code is transforming one list into another list, not performing a block of code for each element of a list. There's a perfectly good way to indicate that you're performing a block of code for each element of a list; why make your code less clear?