Re: How to call a function on each item after a split?
by aitap (Curate) on Oct 08, 2012 at 14:56 UTC
|
| [reply] [Watch: Dir/Any] [d/l] |
|
#!/usr/bin/perl
use strict;
use warnings;
my $str = "item1 | item2| item3
|item4|
";
my @cleaned = map { clean($_) } split( /\|/, $str );
print ">$_<\n" foreach @cleaned;
print "*"x75,"\n";
my @cleaned2 = map { clean($_);$_ } split( /\|/, $str );
print ">$_<\n" foreach @cleaned2;
sub clean
{
chomp($_[0]);
$_[0] =~ s/^\s+//;
$_[0] =~ s/\s+$//;
}
>1<
><
>1<
><
><
**********************************************************************
+*****
>item1<
>item2<
>item3<
>item4<
><
| [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
|
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Why is it better to use the map?
| [reply] [Watch: Dir/Any] |
|
Re: How to call a function on each item after a split?
by nemesdani (Friar) on Oct 08, 2012 at 14:46 UTC
|
Use an array for the items:
my @items = split(/\|/, $str);
foreach my $item(@items){
...#whatever
}
I'm too lazy to be proud of being impatient.
| [reply] [Watch: Dir/Any] [d/l] |
Re: How to call a function on each item after a split?
by Kenosis (Priest) on Oct 08, 2012 at 15:56 UTC
|
Given your data set, another option is to use a regex to grab the items you want out of the string. Consider the following:
use strict;
use warnings;
my $str = "item1 | item2| item3 |item4|";
my @items = $str =~ /\w+/g;
print "$_\n" for @items;
Output:
item1
item2
item3
item4
Hope this helps! | [reply] [Watch: Dir/Any] [d/l] [select] |
Re: How to call a function on each item after a split?
by Lotus1 (Vicar) on Oct 08, 2012 at 15:55 UTC
|
#!/usr/bin/perl
use strict;
use warnings;
my $str = "item1 | item2| item3
|item4|
";
print ">$_<\n" foreach split /\s*\|\s*/, $str;
| [reply] [Watch: Dir/Any] [d/l] |
Re: How to call a function on each item after a split?
by CountZero (Bishop) on Oct 08, 2012 at 15:59 UTC
|
Although using an array is usually a better solution, it can be done as follows:
my ($item1, $item2, $item3, $item4, $item5) = map {clean($_)} split(/\
+|/, $str);
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James My blog: Imperial Deltronics
| [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] [d/l] |
|
Oops, my bad. I am so used to writing subs that return a meaningful result rather than do an in-place action-at-a-distance. MrSnrub has the right solution: just return $_ in the map.
CountZero A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James My blog: Imperial Deltronics
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: How to call a function on each item after a split?
by 2teez (Vicar) on Oct 08, 2012 at 21:40 UTC
|
Hi,
Please, before one shutdown the wonderful solution first given by aitap above on this issue, Or wonder why "the" lovely map function doesn't do the "magic" as expected, Please, check the OP subroutine clean().
sub clean
{
chomp($_[0]);
$_[0] =~ s/^\s+//g;
$_[0] =~ s/\s+$//g;
}
Why use chomp and then sepeartely, remove spaces? At the beginning and endling of each value? I thought, \s+ also covers \n that chomp is suppose to remove?
I modified the OP clean subroutine and both map and grep gave the happy endling. So, there is really no need for the extra $_ as perviously stated, if the modified subroutine is considered. check below:
#!/usr/bin/perl
use strict;
use warnings;
my $str = "item1 | item2| item3
|item4|
";
my @cleaned1 = map { clean_modify($_) } split( /\|/, $str );
my @cleaned2 = grep { clean_modify($_) } split( /\|/, $str );
print join "\n", @cleaned1;
print join "\n", @cleaned2;
sub clean_modify {
$_[0] =~ s/^\s+?|\s+?$//g;
return $_[0];
}
sub clean { ## don't use
chomp( $_[0] );
$_[0] =~ s/^\s+//g;
$_[0] =~ s/\s+$//g;
}
OUTPUT
item1
item2
item3
item4
item1
item2
item3
item4
If you tell me, I'll forget.
If you show me, I'll remember.
if you involve me, I'll understand.
--- Author unknown to me
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
#!/usr/bin/perl
use strict;
use warnings;
my $str = "item1 | 0 | | item2| item3
|item4
|
";
my @cleaned1 = map { clean_no_side_effects($_) } split( /\|/, $str );
my @cleaned2 = grep { clean_no_side_effects($_) } split( /\|/, $str );
print "*"x25, "\n";
print join "\n", @cleaned1;
print "*"x25, "\n";
print join "\n", @cleaned2;
print "*"x25, "\n";
sub clean_no_side_effects {
my $string = shift;
$string =~ s/^\s+|\s+$//g;
return $string;
}
sub clean_modify { ## don't use, still using side effects for grep t
+o work
$_[0] =~ s/^\s+?|\s+?$//g; # '?' isn't needed here since \s neve
+r matches '|'
return $_[0];
}
sub clean { ## don't use
chomp( $_[0] );
$_[0] =~ s/^\s+//g;
$_[0] =~ s/\s+$//g;
}
__END__
*************************
item1
0
item2
item3
item4
*************************
item1
item2
item3
item4
*************************
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: How to call a function on each item after a split?
by MrSnrub (Beadle) on Oct 08, 2012 at 20:40 UTC
|
So, this line:
my ($item1, $item2, $item3, $item4, $item5) = map { clean($_); } split(/\|/, $str);
makes each item either "1" or "" based on (I guess) whether there's empty space between the "item" string and the pipe symbol.
However, this line:
my ($item1, $item2, $item3, $item4, $item5) = map { clean($_); $_; } split(/\|/, $str);
...does EXACTLY what I want in only one line, no matter how many items I have. Many thanks! Question, though: I don't exactly see what is going on with the added $_;. It's not returning a value, since this is not a subroutine. It's not changing the value of $_;. I guess what it does is it tells map to return $_ as opposed to the return value of the previous statement. Is that accurate?
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
map aliases $_ to each item in turn in the list it is dealing with. If you edit $_ in the map the original list items are edited. This can be an unwanted and nasty side effect. Note that aliasing in exactly the same way happens in Perl for loops.
Your clean sub doesn't return the cleaned string. It edits the passed in string in place (Perl essentially passes aliases of parameters into subs). Because the doesn't return the cleaned string you need to "return" the edited string from map, hence the '; $_;' bit in the map.
In this case a cleaner solution is to trim the white space in the split:
my $str = "item1 | item2| item3
|item4|
";
print ">$_<\n" for split /\s*\|\s*/, $str, -1;
Prints:
>item1<
>item2<
>item3<
>item4<
><
True laziness is hard work
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: How to call a function on each item after a split?
by aaron_baugher (Curate) on Oct 09, 2012 at 03:41 UTC
|
You could change your clean sub so it takes an array and returns an array. Then you can 'pipe' your data through it:
#!/usr/bin/env perl
use Modern::Perl;
sub clean {
for (@_){
s/^\s+//;
s/\s+$//; # this includes chomp
}
return @_;
}
my $str = 'The | quick | brown | fox | jumped| over | the | lazy | do
+g. ';
say for clean split /\|/, $str;
Aaron B.
Available for small or large Perl jobs; see my home node.
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: How to call a function on each item after a split?
by kcott (Archbishop) on Oct 09, 2012 at 09:02 UTC
|
G'day MrSnrub,
I see you have a lot of responses already. Here's my take on this.
You can trim the data by capturing everything that isn't leading or trailing whitespace with this regular expression:
/\A\s*(.*?)\s*\z/
You can use that directly inside a map like this:
say for map { (/\A\s*(.*?)\s*\z/) } split /\|/, $str;
You can use it inside a subroutine called from map like this:
say for map { clean($_) } split /\|/, $str;
sub clean { ($_[0] =~ /\A\s*(.*?)\s*\z/)[0] }
It's good that you've tried a variety of cases with whitespace in different positions in your test input $str. What you don't have is leading whitespace at the start of $str or trailing whitespace (following non-whitespace) at the end of $str.
Here's my tests using both of the options I presented with some additional test data.
$ perl -Mstrict -Mwarnings -E '
my $str = " \t item0 with leading whitespace |item1 | item2| item3
| extra item with embedded whitespace \t \n \n\t
|item4|
| itemN with trailing whitespace \t
";
say "---- WITHOUT SUBROUTINE ---";
say ">$_<" for map { (/\A\s*(.*?)\s*\z/) } split /\|/, $str;
say "---- USING SUBROUTINE ---";
say "<$_>" for map { clean($_) } split /\|/, $str;
sub clean { ($_[0] =~ /\A\s*(.*?)\s*\z/)[0] }
'
---- WITHOUT SUBROUTINE ---
>item0 with leading whitespace<
>item1<
>item2<
>item3<
>extra item with embedded whitespace<
>item4<
><
>itemN with trailing whitespace<
---- USING SUBROUTINE ---
<item0 with leading whitespace>
<item1>
<item2>
<item3>
<extra item with embedded whitespace>
<item4>
<>
<itemN with trailing whitespace>
| [reply] [Watch: Dir/Any] [d/l] [select] |