我有两个数组。我需要检查并查看其中一个元素是否出现在另一个元素中。
有没有比嵌套循环更有效的方法呢?我每个都有几千个元素,需要经常运行程序。
答案 0 :(得分:37)
另一种方法是使用Array::Utils
use Array::Utils qw(:all);
my @a = qw( a b c d );
my @b = qw( c d e f );
# symmetric difference
my @diff = array_diff(@a, @b);
# intersection
my @isect = intersect(@a, @b);
# unique union
my @unique = unique(@a, @b);
# check if arrays contain same members
if ( !array_diff(@a, @b) ) {
# do something
}
# get items from array @a that are not in array @b
my @minus = array_minus( @a, @b );
答案 1 :(得分:25)
perlfaq4
救援:
如何计算两个数组的差异?如何计算两个数组的交集?
使用哈希。这是两个以上的代码。它假定每个元素在给定数组中是唯一的:
@union = @intersection = @difference = (); %count = (); foreach $element (@array1, @array2) { $count{$element}++ } foreach $element (keys %count) { push @union, $element; push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element; }
如果正确声明了变量,代码看起来更像是:
my %count;
for my $element (@array1, @array2) { $count{$element}++ }
my ( @union, @intersection, @difference );
for my $element (keys %count) {
push @union, $element;
push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element;
}
答案 2 :(得分:11)
您需要提供更多背景信息。有更有效的方法可以做到:
走出Perl并使用shell(sort
+ comm
)
map
将一个数组转换为Perl哈希,然后循环检索另一个检查哈希成员资格。这具有线性复杂度(“M + N” - 基本上在每个数组上循环一次),而不是具有“M * N”复杂度的嵌套循环
示例:
my %second = map {$_=>1} @second;
my @only_in_first = grep { !$second{$_} } @first;
# use a foreach loop with `last` instead of "grep"
# if you only want yes/no answer instead of full list
使用为您做最后一个项目符号点的Perl模块(注释中提到了List :: Compare)
如果卷非常大并且您需要经常重新比较,则根据添加元素的时间戳来执行此操作。几千个元素还不够大,但我最近不得不区分100k大小的列表。
答案 3 :(得分:7)
你可以试试Arrays::Utils
,它会让它看起来既美观又简单,但它并没有在后端做任何强大的魔法。这是array_diffs
代码:
sub array_diff(\@\@) {
my %e = map { $_ => undef } @{$_[1]};
return @{[ ( grep { (exists $e{$_}) ? ( delete $e{$_} ) : ( 1 ) } @{ $_[0] } ), keys %e ] };
}
由于Arrays::Utils
不是标准模块,因此您需要问自己是否值得安装和维护此模块。否则,它非常接近DVK的答案。
您必须注意某些事项,并且必须在特定情况下定义您想要做的事情。让我们说:
@array1 = qw(1 1 2 2 3 3 4 4 5 5);
@array2 = qw(1 2 3 4 5);
这些数组是否相同?或者,他们是不同的?它们具有相同的值,但@array1
而不是@array2
中存在重复值。
这个怎么样?
@array1 = qw( 1 1 2 3 4 5 );
@array2 = qw( 1 1 2 3 4 5 );
我会说这些数组是相同的,但Array::Utils::arrays_diff
要求不同。这是因为Array::Utils
假设没有重复的条目。
而且,即使mob指出的Perl FAQ也说它假设每个元素在给定数组中都是唯一的。这是你可以做出的假设吗?
无论如何,哈希就是答案。查找哈希很容易,也很快。问题是你想用独特的价值做什么。
这是一个可靠的解决方案,假设重复无关紧要:
sub array_diff {
my @array1 = @{ shift() };
my @array2 = @{ shift() };
my %array1_hash;
my %array2_hash;
# Create a hash entry for each element in @array1
for my $element ( @array1 ) {
$array1_hash{$element} = @array1;
}
# Same for @array2: This time, use map instead of a loop
map { $array_2{$_} = 1 } @array2;
for my $entry ( @array2 ) {
if ( not $array1_hash{$entry} ) {
return 1; #Entry in @array2 but not @array1: Differ
}
}
if ( keys %array_hash1 != keys %array_hash2 ) {
return 1; #Arrays differ
}
else {
return 0; #Arrays contain the same elements
}
}
如果重复确实重要,您需要一种方法来计算它们。这里使用map不仅可以创建由数组中每个元素键入的哈希值,还可以计算数组中的重复项:
my %array1_hash;
my %array2_hash;
map { $array1_hash{$_} += 1 } @array1;
map { $array2_hash{$_} += 2 } @array2;
现在,您可以浏览每个哈希并验证密钥不仅存在,而且条目匹配
for my $key ( keys %array1_hash ) {
if ( not exists $array2_hash{$key}
or $array1_hash{$key} != $array2_hash{$key} ) {
return 1; #Arrays differ
}
}
如果%array1_hash
中的所有条目都与%array2_hash
中的相应条目匹配,则您只会退出for循环。现在,您必须显示%array2_hash
中的所有条目也与%array1_hash
中的条目匹配,并且%array2_hash
没有更多条目。幸运的是,我们可以做我们以前做过的事情:
if ( keys %array2_hash != keys %array1_hash ) {
return 1; #Arrays have a different number of keys: Don't match
}
else {
return; #Arrays have the same keys: They do match
}
答案 4 :(得分:2)
您可以使用它来获取两个数组之间的差异
#!/usr/bin/perl -w
use strict;
my @list1 = (1, 2, 3, 4, 5);
my @list2 = (2, 3, 4);
my %diff;
@diff{ @list1 } = undef;
delete @diff{ @list2 };
答案 5 :(得分:1)
n + n log n算法,如果确定元素在每个数组中都是唯一的(作为散列键)
my %count = ();
foreach my $element (@array1, @array2) {
$count{$element}++;
}
my @difference = grep { $count{$_} == 1 } keys %count;
my @intersect = grep { $count{$_} == 2 } keys %count;
my @union = keys %count;
所以,如果我不确定统一性并希望检查array2中array1元素的存在,
my %count = ();
foreach (@array1) {
$count{$_} = 1 ;
};
foreach (@array2) {
$count{$_} = 2 if $count{$_};
};
# N log N
if (grep { $_ == 1 } values %count) {
return 'Some element of array1 does not appears in array2'
} else {
return 'All elements of array1 are in array2'.
}
# N + N log N
答案 6 :(得分:1)
my @a = (1,2,3);
my @b=(2,3,1);
print "Equal" if grep { $_ ~~ @b } @a == @b;
答案 7 :(得分:0)
尝试使用List:Compare。 IT拥有可在阵列上执行的所有操作的解决方案。 https://metacpan.org/pod/List::Compare
答案 8 :(得分:0)
你想要将@x的每个元素与@y中相同索引的元素进行比较,对吗?这样就可以了。
print "Index: $_ => \@x: $x[$_], \@y: $y[$_]\n"
for grep { $x[$_] != $y[$_] } 0 .. $#x;
...或...
foreach( 0 .. $#x ) {
print "Index: $_ => \@x: $x[$_], \@y: $y[$_]\n" if $x[$_] != $y[$_];
}
您选择哪种类型取决于您是否更有兴趣保留不同元素的索引列表,或者只是对逐个处理不匹配感兴趣。 grep版本可以方便地获取不匹配列表。 (original post)
答案 9 :(得分:0)
不优雅,但易于理解:
#!/usr/local/bin/perl
use strict;
my $file1 = shift or die("need file1");
my $file2 = shift or die("need file2");;
my @file1lines = split/\n/,`cat $file1`;
my @file2lines = split/\n/,`cat $file2`;
my %lines;
foreach my $file1line(@file1lines){
$lines{$file1line}+=1;
}
foreach my $file2line(@file2lines){
$lines{$file2line}+=2;
}
while(my($key,$value)=each%lines){
if($value == 1){
print "$key is in only $file1\n";
}elsif($value == 2){
print "$key is in only $file2\n";
}elsif($value == 3){
print "$key is in both $file1 and $file2\n";
}
}
exit;
__END__