tags:

views:

76

answers:

3

Basically, I have an array, let's say @badvalues.

I have another array, let's say @values.

Basically, I want this:

For each element in @badvalues

  • See if it is in @values
  • If it is, delete it
  • Ultimately, I should end up with either the array @values, containing no elements that are in the array @badvalues, or a new array, @goodvalues, containing every element of @values that is not an element of @badvalues.

I know it sounds simple, and maybe it's because I'm tired, but I can't seem to find a clear answer to this question when searching around.

+7  A: 
# Get only bad values
my %values = map {$_=>1} @values;
my @new_badvalues = grep { !$values{$_} } @badvalues;

# Get only good values
my %badvalues = map {$_=>1} @badvalues;
my @goodvalues = grep { !$badvalues{$_} } @values;

# An alternative
@badvalues{@badvalues} = ();
foreach $item (@values) {
    push(@goodvalues, $item) unless exists $badvalues{$item};
}

For more complete reference, please see "Chapter 4.7. Finding Elements in One Array but Not Another" of "Perl Cookbook"

DVK
Thank you! Virtual brownie for you.
Interwebs
`hash` brownie? :)
DVK
Stackoverflow: making simultaneous references to weed and Perl since 2010!
Interwebs
+1  A: 

If you've got a modern Perl version, like say >= 5.10.1, then you can also do this:

my @final_good = grep { !($_ ~~ @badvalues ) } @values;

or for clearer precendence:

my @final_good = grep { not $_ ~~ @badvalues } @values;

This is using the smartmatch operator that was added in Perl 5.10.

szbalint
I wish the Perl I got to work with was that modern, I'm on 5.6.1 :(Thanks for the tip tho, I'll remember that.
Interwebs
@Interwebs - I'd feel bad for you, but some of our stuff is still stuck on 5.005. Shame on us!
DVK
+1  A: 

Little bit faster and less memory consuming version of approach shown in DVK's answer

my %badvalues;
@badvalues{@badvalues} = ();
my @goodvalues = grep !exists $badvalues{$_}, @values;
Hynek -Pichi- Vychodil
I doubt there exists code in the world, except a few pathological cases, where doing the above optimization would be noticeably beneficial. However, it degrades readability.
szbalint
@Hynek - this code is taking the hash generator from my second example followed by hash usage from my first example :). Still a valid code, of course - this comment is meant as an excuse of why I didn't include this solution as well; and not a criticism of yours :)
DVK
@szbalint: You can be surprised but there is practical reasons to write code like this. It is lot of easier write this sort of perl code to get 38% more performance and same 38% less memory footprint instead write much less readable C code in XS module. This almost two fifth of speed can be difference between usable and unusable in real project. And if you compare `my %badvalues = map {$_=>1} @badvalues;` and `my %badvalues; @badvalues{@badvalues} = ();` there is not so much difference in readability.
Hynek -Pichi- Vychodil
@szbalint: depends on the reader. If you are familiar with the idiom, it's easier to read the empty-assignment-to-hash-slice than the map.
ysth