Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Using regex in Map function

by narashima (Beadle)
on May 02, 2007 at 20:57 UTC ( [id://613264]=perlquestion: print w/replies, xml ) Need Help??

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

Revered Monks, I have a csv file that has 2 comma seperated values.
eg. a, b
c, d
e, f

I am trying to slurp this file in and append a string(in this example the string I am trying to append is 'hi' ) to these values. Below is what I am doing
@new= map {s/$_/$_,hi/} <TEST>;
I am expecting something like:
a, b, hi
c, d, hi
e, f, hi

But this does not work. When I dump @new using Data::Dumper I get the following output:
$VAR1 = 1;
$VAR2 = 1;
$VAR3 = 1;

Could someone please tell me what I am doing wrong?

Thanks!

Replies are listed 'Best First'.
Re: Using regex in Map function
by merlyn (Sage) on May 02, 2007 at 22:21 UTC
    You'd probably be even more surprised by the result of this:
    my @in = qw(aaa bbb ccc); my @out = map { s/.$/x/ } @in; print "@in\n=>\n@out\n";
    which prints:
    aax bbx ccx => 1 1 1
    Yeah, the original is modified. Basically: never modify $_ in a map, and remember that the "output" of s/// is the success count/code. To get what you want, you need to localize $_ and reuse it as the last expression evaluated in the block:
    my @in = qw(aaa bbb ccc); my @out = map { local $_ = $_; s/.$/x/; $_ } @in; print "@in\n=>\n@out\n";
    which indeed shows:
    aaa bbb ccc => aax bbx ccx

      or better perhaps:

      my @out = map {substr ($_, 0, -1) . 'x'} @in;

      Prints:

      aaa bbb ccc => aax bbx ccx

      DWIM is Perl's answer to Gödel
      To get what you want, you need to localize $_ and reuse it as the last expression evaluated in the block:
      my @in = qw(aaa bbb ccc); my @out = map { local $_ = $_; s/.$/x/; $_ } @in; print "@in\n=>\n@out\n";

      Or use a proper lexical variable:

      my @out = map { (my $s = $_) =~ s/.$/x/; $s } @in;

      AIUI in blead/5.10 the following should also work just in the same manner:

      my $_; # ... my @out = map { s/.$/x/; $_ } @in;
        Hi Blazer & Randal,

        Could you guys please explain why we need to localize $_ inside the map? I am not sure I understand why we need to do this.I seem to get the same result even if I do not localize $_. Is there something I am missing?

        Thanks!
      ************** Bad Program ****
      my @in = qw(a b c d e f g h); print "Start: ",@in,"\n","***RUNNING MAP***\n\n"; my @out = map { s/.$/x/} @in; print "in : ",@in,"\n"; print "out: ",@out,"\n"
      ************* Output ******
      Start: abcdefgh ***RUNNING MAP*** in : xxxxxxxx out: 11111111
      *********************
      Comment #1:
      Use "r" feature on regex
      ************** fixed Program ****
      my @in = qw(a b c d e f g h); print "Start: ",@in,"\n","***RUNNING MAP***\n\n"; my @out = map { s/.$/x/r} @in; print "in : ",@in,"\n"; print "out: ",@out,"\n"
      ************* Output ******
      Start: abcdefgh ***RUNNING MAP*** in : abcdefgh out: xxxxxxxx
      ****************
      Comment #2

      But what if you REALLY wanted the return value of the regex - and not modify the original output? (why? I don't know)
      Localize the input...

      ************** fixed Program ****
      my @in = qw(a b c d e f g h); print "Start: ",@in,"\n","***RUNNING MAP***\n\n"; my @out = map { s/.$/x/} my @temp = @in; print "in : ",@in,"\n"; print "out: ",@out,"\n"
      ************* Output ******
      Start: abcdefgh ***RUNNING MAP*** in : abcdefgh out: 11111111
        You're replying to my note from 2007 with a feature that has only been available for a few releases. Nice.

        -- Randal L. Schwartz, Perl hacker

        The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Re: Using regex in Map function
by jdporter (Paladin) on May 02, 2007 at 21:09 UTC

    You need to remember that the result of map is the list of the final value of each pass through its block. The value of s/// is a boolean indicating whether a replacement was made. You want the final value of the map block to be $_. Like so:

    @new = map { s/$_/$_,hi/; $_ } <TEST>;

    However, your substitution is less than ideal. In particular, you'll have problems if $_ contains any regex-special characters. If you're simply appending, you could do this:

    @new = map { s/$/,hi/; $_ } <TEST>;
    or, even betterworse, this:
    @new = map { $_ . ',hi' } <TEST>;

    A word spoken in Mind will reach its own level, in the objective world, by its own weight
      The value of s/// is a boolean indicating whether a replacement was made.

      Err, no, not really. It's actually the number of replacements made. Thus:

      # given $x = 'abacad'; # then $x =~ s/a/z/; # returns 1 $x =~ s/a/z/g; # returns 3 $x =~ s/x/z/; # returns empty string, not undef

      ... although it can be treated quite nicely in a boolean manner :)

      • another intruder with the mooring in the heart of the Perl

      In addition to what jdporter is saying, I'd like to add that this is probably faster and clearer:

      @new = <TEST>; s/something/something/ for @new;

      I haven't Benchmarked it or anything, but I'm pretty sure it's faster than map {s/regex/stuff/} — either way, it's probably more readable.

      -Paul

      or, even better, this
      That may be subject to judgement.

      While your 1st and 3rd variant append behind the line-ending newline, the 2nd is probably closer to what the OP wanted, since $ is matching before that newline.

      If you need something like the copy - do something more often, you could also create a copy-wrapper for map, let's call it cwmap:

      sub cwmap( &@ ) { map { local $_ = $_; # create copy $_[0]->(); # execute code-block $_; # copied element } @_[ 1..$#_ ]; # list } # cwmap # and then use it like map: @new = cwmap { s/$_/$_,hi/ } <TEST>;

      Best regards,
      perl -e "s>>*F>e=>y)\*martinF)stronat)=>print,print v8.8.8.32.11.32"

Re: Using regex in Map function
by BrowserUk (Patriarch) on May 02, 2007 at 23:13 UTC
Re: Using regex in Map function
by naikonta (Curate) on May 03, 2007 at 04:44 UTC
    I'm not really sure about your exact requirement, but should it's really that simple regex, simple concatenation will do as well.
    my @orig = qw(a,b c,d e,f); my $extra = 'hi'; my @new = map { $_ .= ",$extra" } @orig; print "orig: [@orig] => new: [@new]\n"; # orig: [a,b,hi c,d,hi e,f,hi] => new: [a,b,hi c,d,hi e,f,hi]
    This way, you append the line and return it as well from map().

    Open source softwares? Share and enjoy. Make profit from them if you can. Yet, share and enjoy!

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (2)
As of 2024-04-19 19:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found