tags:

views:

282

answers:

8

I have two arrays, @a and @b. I want to do a compare among the elements of the two arrays.

my @a = qw"abc def efg ghy klm ghn";
my @b = qw"def ghy jgk lom com klm";

If any element matches then set a flag. Is there any simple way to do this?

+3  A: 

List::Compare

if ( scalar List::Compare->new(\@a, \@b)->get_intersection ) {
    …
}
David Dorward
+5  A: 

First of all, your 2 arrays need to be written correctly.

@a = ("abc","def","efg","ghy","klm","ghn");
@b = ("def","efg","ghy","klm","ghn","klm");

Second of all, for arbitrary arrays (e.g. arrays whose elements may be references to other data structures) you can use Data::Compare.

For arrays whose elements are scalar, you can do comparison using List::MoreUtils pairwise BLOCK ARRAY1 ARRAY2, where BLOCK is your comparison subroutine. You can emulate pairwise (if you don't have List::MoreUtils access) via:

if (@a != @b) {
    $equals = 0;
} else {
    $equals = 1;
    foreach (my $i = 0; $i < @a; $i++) {
        # Ideally, check for undef/value comparison here as well 
        if ($a[$i] != $b[$i]) { # use "ne" if elements are strings, not numbers
                                # Or you can use generic sub comparing 2 values
            $equals = 0;
            last;
        }
    }
}

P.S. I am not sure but List::Compare may always sort the lists. I'm not sure if it can do pairwise comparisons.

DVK
You are using `scalar` way too liberally for my taste.
Sinan Ünür
Scalar comparison operators impose scalar context on their arguments. So, `@a == @b` is the same as `scalar(@a) == scalar(@b)` and `$i < @a` is the same as `$i < scalar(@a)`.
Sinan Ünür
That was for demo readability... i almost never use scalar() in production code, for just the reason you didn't like it here. if you think it doesn't help much, I can edit it out
DVK
This is very subjective. I think extra fluff detracts from readability (otherwise, I would still be programming in Java ;-)
Sinan Ünür
Heh...valid point. Replaced...
DVK
I haven't double checked that it works, but why wouldn't `@a = qw"abc def efg ghy klm ghn"` work? Wouldn't it just be parsed as a list inside the user-defined delineator ('`"`' in this case?) It shouldn't be any different than `qw//` or `qw||` or `qw()` or whatnot.
Oesor
@Oesor - you're not using Sinan's Time::Machine module. My comment was aimed at the original wording of the question (before brian's edit) that said `my @a = "abc,def,efg,ghy,klm,ghn"`
DVK
+1  A: 
my @a = qw' abc def efg ghy klm ghn ';
my @b = qw' def ghy jgk lom com klm ';

my $flag;

foreach  my $item(@a) {
  $flag = @b~~$item ? 0 : 1;
  last if !$flag;
}

Note that you will need Perl 5.10, or later, to use the smart match operator (~~) .

Mike
Mike - will this work before Perl 5.10?
DVK
@DVK, I dunno. I learnt this from Learning Perl. I don't need the use 5.010; statement but maybe it's only workable after Perl 5.10. I'll check it out. I'm still a Perl learner. Kindly correct me if anything went wrong :)
Mike
@DVK, the book says "Perl 5.10's smart match operator". But it looks like that I don't have to use the use 5.010; statement. Just tested it again and the use 5.010; statement is unnecessary at least with ActivePerl 5.10.0 on WinXP. But I suppose this won't work before Perl 5.10.
Mike
While I don't see any glaring newbie mistakes, I don't think this will work correctly all of the time.
Brad Gilbert
A: 

From the requirement that 'if any element matches', use the intersection of sets:

sub set{
  my %set = map { $_, undef }, @_;
  return sort keys %set;
}
sub compare{
    my ($listA,$listB) = @_;
    return ( (set(@$listA)-set(@$listB)) > 0)
}
Phil H
This question is tagged "perl". I don't want to downvote you without warning you first ...
Kinopiko
No question is entirely language-specific. I'm sure someone can come up with a perl version of it. It also illustrates the general point.
Phil H
Yes, but what if a newbie sees your answer, in this perl-tagged thread, without any notification that it isn't a Perl answer, and types it in, then wonders why it doesn't work? The only responsible thing to do with this answer is to downvote it. Sorry.
Kinopiko
@Brad: Cheers, that looks like a nice chunk. I'm surprised there isn't a builtin/widely available module for sets...
Phil H
I removed my downvote. There is still a downvote but it's not mine.
Kinopiko
+1  A: 

This is one way:

use warnings;
use strict;
my @a = split /,/, "abc,def,efg,ghy,klm,ghn";
my @b = split /,/, "def,ghy,jgk,lom,com,klm";
my $flag = 0;
my %a;
@a{@a} = (1) x @a;
for (@b) {
    if ($a{$_}) {
        $flag = 1;
        last;
    }
}
print "$flag\n";
Kinopiko
Not the easiest read but I like it.When answering a question I like explain more offbeat behavior so we learn instead of cookbooking though.
HerbN
A: 

Brute force should do the trick for small a n:

my $flag = 0;
foreach my $i (@a) {
    foreach my $k (@b) {
        if ($i eq $k) {
            $flag = 1;
            last;
        }
    }
}

For a large n, use a hash table:

my $flag   = 0;
my %aa     = ();
   $aa{$_} = 1 foreach (@a);
foreach my $i (@b) {
    if ($aa{$i}) {
        $flag = 1;
        last;
    }
}

Where a large n is |@a| + |@b| > ~1000 items

dsm
+1  A: 

Check to create an intersect function, which will return a list of items that are present in both lists. Then your return value is dependent on the number of items in the intersected list.

You can easily find on the web the best implementation of intersect for Perl. I remember looking for it a few years ago.

Here's what I found :


my @array1 = (1, 2, 3);
my @array2 = (2, 3, 4);
my %original = ();
my @isect = ();

map { $original{$_} = 1 } @array1;
@isect = grep { $original{$_} } @array2;

David Brunelle
A: 

IMHO, you should use List::MoreUtils::pairwise. However, if for some reason you cannot, then the following sub would return a 1 for every index where the value in the first array compares equal to the value in the second array. You can generalize this method as much as you want and pass your own comparator if you want to, but at that point, just installing List::MoreUtils would be a more productive use of your time.

use strict; use warnings;

my @a = qw(abc def ghi jkl);
my @b = qw(abc dgh dlkfj jkl kjj lkm);
my $map = which_ones_equal(\@a, \@b);

print join(', ', @$map), "\n";

sub which_ones_equal {
    my ($x, $y, $compare) = @_;
    my $last = $#$x > $#$y ? $#$x : $#$y;
    no warnings 'uninitialized';
    return [ map { 0 + ($x->[$_] eq $y->[$_]) } $[ .. $last ];
}
Sinan Ünür