Re: Best way to remove elem of for loop
by hdb (Monsignor) on Apr 24, 2014 at 14:41 UTC
|
@ar = grep { -d $_ } @ar;
| [reply] [d/l] |
|
|
Hmm, that's good. I had made the link between 'map' and 'grep' being similar, but it hadn't sunk in that you can apply an EXPR to grep, as well as a pattern.
| [reply] |
Re: Best way to remove elem of for loop
by Preceptor (Deacon) on Apr 24, 2014 at 14:41 UTC
|
What you're doing is inserting '' into your array. You probably don't want to do this.
I would generally suggest you don't want to try and modify your array, and instead consider just creating a new one, with the filtered elements.
In 'foreach' terms, because 'map' makes my brain hurt:
my @filtered;
foreach my $element ( @arg ) {
if ( -d $element ) { push ( @filtered, $element ); };
}
If you really want to do an inplace modification, then you may want to look at setting your element 'undef'.
With map, this would probably be something like (for the sake of my own curiosity):
my @filtered = map { -d $_ ? $_ : () } @ar;
.... I think. Anyway, I prefer the former which I consider way more readable. | [reply] [d/l] [select] |
|
|
my @filtered = grep { -d $_ } @ar;
# or
my @filtered = grep -d $_, @ar;
map() means "process the elements one after another and give me the results", while grep() means "give me only those elements of the list that match the condition".
Jenda
Enoch was right!
Enjoy the last years of Rome.
| [reply] [d/l] |
|
|
my @filtered = grep -d, @ar;
use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name
| [reply] [d/l] |
|
|
Yes, spot on. I still think of grep in Unix terms, e.g. pattern matching. I had blanked out the bit where it can take an EXPR to evaluate. Even if I did know that underneath the hood grep and map aren't much different.
| [reply] |
|
|
Oh, nice
Something similar to your code (but more primitive) was what I had been doing. I noticed the same result by doing the nul push (as you mentioned) and I thought.. ". well... its shorter ..." but I see your point. And the case posted where it had the result I wanted is way too isolated a case to use that technique at all.
So its time well spent ... filtering/creating creating new @ar... thanks
| [reply] |
Re: Best way to remove elem of for loop
by rjt (Curate) on Apr 24, 2014 at 16:47 UTC
|
Of the solutions already posted, none of them seem to address the fact that you also wanted to print an error message when removing one of the elements from the list. Here's an elegant way to do that:
my @dirs = qw#/usr /tmp Kitten.gif /sys /var/run /nonexistent /root#;
@dirs = map {
warn "`$_' is not a directory\n" if not -d;
-d _ ? $_ : ()
} @dirs;
print "\nThe following are valid: @dirs\n";
Output:
`Kitten.gif' is not a directory
`/nonexistent' is not a directory
The following are valid: /usr /tmp /sys /var/run /root
The major point of learning, here, is probably the fact that map uses the result of the last statement in the block: in this case, the result of the conditional operator: either the directory name, or an empty list, if it's not a directory.
Edit: Thanks RonW for reminding me of _. I left my original paragraph below the <readmore> for continuity's sake, but have updated the code above—and had my 1st cup of coffee.
| [reply] [d/l] [select] |
|
|
| [reply] [d/l] |
|
|
$ ls -F
fred testFBF* xxx/ yyy/
$ perl -Mstrict -Mwarnings -E '
my @dirs = qw{ fred testFBF xxx yyy };
@dirs = map {
-d $_
? $_
: do { warn qq{Not a directory: $_\n}; () }
} @dirs;
say qq{Directories: @dirs};'
Not a directory: fred
Not a directory: testFBF
Directories: xxx yyy
$
I hope this is of interest.
| [reply] [d/l] [select] |
Re: Best way to remove elem of for loop
by InfiniteSilence (Curate) on Apr 24, 2014 at 14:44 UTC
|
You method does not remove the total number of elements in the array which can potentially cause problems in the future:
...
#assume @ar is filled with this stuff
__END__
/tmp
/var
/etc
/nonexistent
/home
/nonexistent
DB<5> p Dumper \@ar
$VAR1 = [
'/tmp',
'/var',
'/etc',
'',
'/home',
''
];
Celebrate Intellectual Diversity
| [reply] [d/l] [select] |
Re: Best way to remove elem of for loop
by locked_user sundialsvc4 (Abbot) on Apr 24, 2014 at 15:38 UTC
|
I would suggest that you should loop through your list of entries, and push onto a separate list those which are directories. Or, simply use a File::Find loop (instead of building an array of things, which consumes memory arbitrarily), and push directories onto the array as they are found. (“Millions of files” would result in “a big phat array,” with the thrashing-induced slowdowns that such a thing might produce, whereas there probably aren’t “millions of directories.”)
Yes, it is perfectly reasonable to “mark things,” too, and then to loop back through looking for things that are not so marked. Thus, your approach of zapping the names that are not directories is a legitimate approach. However, I would set the element to undef, since you only need to get rid of the value that is now there; you need not conjure up an empty-string value to replace it. The “marking” approach avoids the issue of diddling with an array while you are looping through one, which in general is never prudent to do.
| |