Reputation: 159
I'm looping through an array, and I want to test if an element is found in another array.
In pseudo-code, what I'm trying to do is this:
foreach $term (@array1) {
if ($term is found in @array2) {
#do something here
}
}
I've got the "foreach" and the "do something here" parts down-pat ... but everything I've tried for the "if term is found in array" test does NOT work ...
I've tried grep:
if grep {/$term/} @array2 { #do something }
# this test always succeeds for values of $term that ARE NOT in @array2
if (grep(/$term/, @array2)) { #do something }
# this test likewise succeeds for values NOT IN the array
I've tried a couple different flavors of "converting the array to a hash" which many previous posts have indicated are so simple and easy ... and none of them have worked.
I am a long-time low-level user of perl, I understand just the basics of perl, do not understand all the fancy obfuscated code that comprises 99% of the solutions I read on the interwebs ... I would really, truly, honestly appreciate any answers that are explicit in the code and provide a step-by-step explanation of what the code is doing ...
... I seriously don't grok $_ and any other kind or type of hidden, understood, or implied value, variable, or function. I would really appreciate it if any examples or samples have all variables and functions named with clear terms ($term as opposed to $_) ... and describe with comments what the code is doing so I, in all my mentally deficient glory, may hope to possibly understand it some day. Please. :-)
...
I have an existing script which uses 'grep' somewhat succesfully:
$rc=grep(/$term/, @array);
if ($rc eq 0) { #something happens here }
but I applied that EXACT same code to my new script and it simply does NOT succeed properly ... i.e., it "succeeds" (rc = zero) when it tests a value of $term that I know is NOT present in the array being tested. I just don't get it.
The ONLY difference in my 'grep' approach between 'old' script and 'new' script is how I built the array ... in old script, I built array by reading in from a file:
@array=`cat file`;
whereas in new script I put the array inside the script itself (coz it's small) ... like this:
@array=("element1","element2","element3","element4");
How can that result in different output of the grep function? They're both bog-standard arrays! I don't get it!!!! :-(
########################################################################The term I'm trying to match/find/grep is a word element, for example "word123".
This exercise was just intended to be a quick-n-dirty script to find some important info from a file full of junk, so I skip all the niceties (use strict, warnings, modules, subroutines) by choice ... this doesn't have to be elegant, just simple.
The term I'm searching for is stored in a variable which is instantiated via split:
foreach $line(@array1) {
chomp($line); # habit
# every line has multiple elements that I want to capture
($term1,$term2,$term3,$term4)=split(/\t/,$line);
# if a particular one of those terms is found in my other array 'array2'
if (grep(/$term2/, @array2) {
# then I'm storing a different element from the line into a 3rd array which eventually will be outputted
push(@known, $term1) unless $seen{$term1}++;
}
}
see that grep up there? It ain't workin right ... it is succeeding for all values of $term2 even if it is definitely NOT in array2 ... array1 is a file of a couple thousand lines. The element I'm calling $term2 here is a discrete term that may be in multiple lines, but is never repeated (or part of a larger string) within any given line. Array2 is about a couple dozen elements that I need to "filter in" for my output.
...
I just tried one of the below suggestions:
if (grep $_ eq $term2, @array2)
And this grep failed for all values of $term2 ... I'm getting an all or nothing response from grep ... so I guess I need to stop using grep. Try one of those hash solutions ... but I really could use more explanation and clarification on those.
Upvotes: 13
Views: 55324
Reputation: 21
1. grep with eq , then
if (grep {$_ eq $term2} @array2) {
print "$term2 exists in the array";
}
2. grep with regex , then
if (grep {/$term2/} @array2) {
print "element with pattern $term2 exists in the array";
}
Upvotes: 0
Reputation: 2200
Pattern matching is the most efficient way of matching elements. This would do the trick. Cheers!
print "$element found in the array\n" if ("@array" =~ m/$element/);
Upvotes: 1
Reputation: 1
The example below might be helpful, it tries to see if any element in @array_sp is present in @my_array:
#! /usr/bin/perl -w
@my_array = qw(20001 20003);
@array_sp = qw(20001 20002 20004);
print "@array_sp\n";
foreach $case(@my_array){
if("@array_sp" =~ m/$case/){
print "My God!\n";
}
}
use pattern matching can solve this. Hope it helps -QC
Upvotes: 0
Reputation: 2783
#!/usr/bin/perl
@ar = ( '1','2','3','4','5','6','10' );
@arr = ( '1','2','3','4','5','6','7','8','9' ) ;
foreach $var ( @arr ){
print "$var not found\n " if ( ! ( grep /$var/, @ar )) ;
}
Upvotes: 2
Reputation: 6652
Your 'actual code' shouldn't even compile:
if (grep(/$term2/, @array2) {
should be:
if (grep (/$term2/, @array2)) {
You have unbalanced parentheses in your code. You may also find it easier to use grep with a callback (code reference) that operates on its arguments (the array.) It helps keep the parenthesis from blurring together. This is optional, though. It would be:
if (grep {/$term2/} @array2) {
You may want to use strict; and use warnings; to catch issues like this.
Upvotes: 0
Reputation: 67900
This is in perlfaq. A quick way to do it is
my %seen;
$seen{$_}++ for @array1;
for my $item (@array2) {
if ($seen{$item}) {
# item is in array2, do something
}
}
If letter case is not important, you can set the keys with $seen{ lc($_) }
and check with if ($seen{ lc($item) })
.
ETA:
With the changed question: If the task is to match single words in @array2
against whole lines in @array1
, the task is more complicated. Trying to split the lines and match against hash keys will likely be unsafe, because of punctuation and other such things. So, a regex solution will likely be the safest.
Unless @array2
is very large, you might do something like this:
my $rx = join "|", @array2;
for my $line (@array1) {
if ($line =~ /\b$rx\b/) { # use word boundary to avoid partial matches
# do something
}
}
If @array2
contains meta characters, such as *?+|
, you have to make sure they are escaped, in which case you'd do something like:
my $rx = join "|", map quotemeta, @array2;
# etc
Upvotes: 9
Reputation: 44344
You could use the (infamous) "smart match" operator, provided you are on 5.10 or later:
#!/usr/bin/perl
use strict;
use warnings;
my @array1 = qw/a b c d e f g h/;
my @array2 = qw/a c e g z/;
print "a in \@array1\n" if 'a' ~~ @array1;
print "z in \@array1\n" if 'z' ~~ @array1;
print "z in \@array2\n" if 'z' ~~ @array2;
The example is very simple, but you can use an RE if you need to as well. I should add that not everyone likes ~~ because there are some ambiguities and, um, "undocumented features". Should be OK for this though.
Upvotes: 6
Reputation: 241738
This should work.
#!/usr/bin/perl
use strict;
use warnings;
my @array1 = qw/a b c d e f g h/;
my @array2 = qw/a c e g z/;
for my $term (@array1) {
if (grep $_ eq $term, @array2) {
print "$term found.\n";
}
}
Output:
a found.
c found.
e found.
g found.
Upvotes: 5