CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Beginner:
map function

 



ashish_chand
Novice

Apr 11, 2013, 9:56 AM

Post #1 of 8 (722 views)
map function Can't Post

sub csv_join {
my @row = map { defined $_ ? $_ : '' } @_;
return join (",",
map { s/([\n,])|"/ $1 || '""' /ge and qq["$_"] or $_ } @row); -- please explain this line
}


Laurent_R
Veteran / Moderator

Apr 11, 2013, 10:55 AM

Post #2 of 8 (719 views)
Re: [ashish_chand] map function [In reply to] Can't Post

The map function applies the following code:


Code
{ s/([\n,])|"/ $1 || '""' /ge and qq["$_"] or $_ }


to each element of the @row aray and teturns to the join function a list of the elements thus modified by aforesdaid code.


BillKSmith
Veteran

Apr 11, 2013, 1:42 PM

Post #3 of 8 (706 views)
Re: [ashish_chand] map function [In reply to] Can't Post

This line is very complicated. Let me break it up some.

Code
sub csv_join { 
my @row = map { defined $_ ? $_ : '' } @_;
return join(
",",
map {
s/([\n,])|"/ $1 || '""' /ge
and qq["$_"]
or $_
} @row,
)
}


As Laurent said, the map block is applied to each element of @row.

The first line of the map block is a substitution which replaces every double quote character with two of them.
As a side effect, it also returns TRUE if the string contains a comma or newline.

The AND line puts double quotes around the string if the first result is TRUE.

The OR line returns the original string if the first result is not TRUE.


Now you want to know how the substitution does its magic. If it finds a comma or a newline, it matches and puts that character in $1. If it finds a double quote character, it matches, but leaves $1 undefined. It then replaces that character with the entire match field. Because of the /e, it evaluates that field. If the character is a comma or a newline, $1 is defined so the character ends up replacing itself. If the character is a double quote, $1 is undefined (FALSE). The string consisting of two double quotes then replaces the one double quote character. This process is repeated for every occurence of these three characters because of the /g.

Got that?
Good Luck,
Bill


ashish_chand
Novice

Apr 11, 2013, 7:53 PM

Post #4 of 8 (698 views)
Re: [BillKSmith] map function [In reply to] Can't Post

hi Bill,

Thanks for your updated on this.

As you have mentioned that in case if comma or new line is found $1 willl be defined and in case of double quote, $1 will be undifined and two double quote will replace a single double quote.

so how this logic is working, i mean how its being judge that when it found double quote $1 will be undefined?

and i am not sure how | and || are being used here logically.

thanks & regards,

Ashish chand


BillKSmith
Veteran

Apr 11, 2013, 9:15 PM

Post #5 of 8 (693 views)
Re: [ashish_chand] map function [In reply to] Can't Post

First read the documentation on the operator s/PATTERN/REPLACEMENT/ in perldoc perlop.

If the PATTERN matches anything in your string, it is replaced by the REPLACEMENT. In your case, the PATTERN is a fairly simple Regular Expression. The pipe character in the regex means that the regex matches if the pattern on either side of it matches. On the right side is just a double quote. On the left, we find a charachter class consisting of a newline and a comma. It will match either one. The character class is in parenthesis. They mean put the matched character in $1. (Note that a double quote character does match the pattern, but not anything in the parenthesis hence $1 is not defined.)

The matching character is replaced by the REPLACEMENT string.
Because of the /e, the REPLACEMENT string is evaluated as if it were perl code. The two pipe characters are the logical-or operator in perl. If the first argument ($1 in this case) is true, it is returned. This happens for comma or newline. In the case of the double quote character, $1 is undefined (FALSE) so the second arument (A string consisting of two double quotes) is returned. In all three cases, the returned string replaces the original matched character. The only change to the string at this point is that double quotes have been replaced with a pair of double quotes. (The other two characters were 'replaced' with themselves.)

The remained of the map block return the string with double quotes around it if it was matched. Otherwise it just returns the original string.
Good Luck,
Bill


Laurent_R
Veteran / Moderator

Apr 12, 2013, 12:44 AM

Post #6 of 8 (682 views)
Re: [BillKSmith] map function [In reply to] Can't Post

What puzzles me, though, with this regex substitution is why the author did not simply use something like:


Code
s/"/""/g



FishMonger
Veteran / Moderator

Apr 12, 2013, 6:36 AM

Post #7 of 8 (672 views)
Re: [Laurent_R] map function [In reply to] Can't Post

What puzzles me even more is why the author went with such a sloppy/obfuscated approach instead of using the methods provided in Text::CSV.

one example method

Code
$csv->combine(@columns); 
my $new_csv = $csv->string;



(This post was edited by FishMonger on Apr 12, 2013, 6:39 AM)


BillKSmith
Veteran

Apr 12, 2013, 6:44 AM

Post #8 of 8 (670 views)
Re: [Laurent_R] map function [In reply to] Can't Post

I would simplify the whole subroutine.

Code
sub csv_join { 
my $QUOTE = q(");
return join(
q(,),
map {
my $row = $_;
$row //= q();
if ($row =~ m/[",\n]/){
$row =~ s/$QUOTE/$QUOTE$QUOTE/g;
$row = qq($QUOTE$row$QUOTE);
}
$row;
} @_
);
}


Perhaps I went to extremes to avoid the forest of quote marks.
Good Luck,
Bill

(This post was edited by BillKSmith on Apr 12, 2013, 7:23 AM)

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives