arrays - 识别一个哈希数组中不在另一个哈希数组中的元素(perl)
问题描述
我是一名新手 perl 程序员,试图识别哪些元素在一个哈希数组中但不在另一个哈希数组中。我正在尝试搜索“新”数组,识别“旧”数组中不存在的 id、标题和创建的元素。
我相信我可以使用一组基本的 for() 循环,但我想更有效地做到这一点。这只是在尝试使用 grep() 并失败后才出现的。
这些数组是从这样的数据库构建的:
use DBI;
use strict;
use Data::Dumper;
use Array::Utils qw(:all);
sub db_connect_new();
sub db_disconnect_new($);
sub db_connect_old();
sub db_disconnect_old($);
my $dbh_old = db_connect_old();
my $dbh_new = db_connect_new();
# get complete list of articles on each host first (Joomla! system)
my $sql_old = "select id,title,created from mos_content;";
my $sql_new = "select id,title,created from xugc_content;";
my $sth_old = $dbh_old->prepare($sql_old);
my $sth_new = $dbh_new->prepare($sql_new);
$sth_old->execute();
$sth_new->execute();
my $ref_old;
my $ref_new;
while ($ref_old = $sth_old->fetchrow_hashref()) {
push @rv_old, $ref_old;
}
while ($ref_new = $sth_new->fetchrow_hashref()) {
push @rv_new, $ref_new;
}
my @seen = ();
my @notseen = ();
foreach my $i (@rv_old) {
my $id = $i->{id};
my $title = $i->{title};
my $created = $i->{created};
my $seen = 0;
foreach my $j (@rv_new) {
if ($i->{id} == $j->{id}) {
push @seen, $i;
$seen = 1;
}
}
if ($seen == 0) {
print "$i->{id},$i->{title},$i->{state},$i->{catid},$i->{created}\n";
push @notseen, $i;
}
}
使用 Dumper(@rv_old) 打印数组时,数组如下所示:
$VAR1 = {
'title' => 'Legal Notice',
'created' => '2004-10-07 00:17:45',
'id' => 14
};
$VAR2 = {
'created' => '2004-11-15 16:04:06',
'id' => 86096,
'title' => 'IRC'
};
$VAR3 = {
'id' => 16,
'created' => '2004-10-07 16:15:29',
'title' => 'About'
};
我尝试使用 grep() 使用数组引用,但我认为我对数组、哈希和引用的理解不够好,无法正确执行。我失败的 grep() 尝试如下。我将不胜感激有关如何正确执行此操作的任何想法。
我相信这个问题是我不知道如何引用第二个哈希数组中的 id 字段。我见过的大多数使用 grep() 的示例只是查看整个数组,就像使用常规 grep(1) 一样。我需要遍历一个数组,检查 id 字段中的每个值与另一个数组中的 id 字段。
my $rv_old_ref = \@rv_old;
my $rv_new_ref = \@rv_new;
for my $i ( 0 .. $#rv_old) {
my $match = grep { $rv_new_ref->$_ == $rv_old_ref->$_ } @rv_new;
push @notseen, $match if !$match;
}
我还尝试了上面 grep() 的变体:
1) if (($p) = grep ($hash_ref->{id}, @rv_old)) {
2) if ($hash_ref->{id} ~~ @rv_old) {
解决方案
有许多比较数组的库。但是,您的比较涉及复杂的数据结构(数组具有 hashrefs 作为元素),这至少使我知道的所有模块的使用变得复杂。
因此,这是一种手动完成的方法。我使用显示的数组及其更改了一个值的副本。
use warnings;
use strict;
use feature 'say';
use List::Util qw(none); # in List::MoreUtils with older Perls
use Data::Dump qw(dd pp);
sub hr_eq {
my ($e1, $e2) = @_;
return 0 if scalar keys %$e1 != scalar keys %$e2;
foreach my $k1 (keys %$e1) {
return 0 if !exists($e2->{$k1}) or $e1->{$k1} ne $e2->{$k1};
}
return 1
}
my @a1 = (
{ 'title' => 'Legal Notice', 'created' => '2004-10-07 00:17:45', 'id' => 14 },
{ 'created' => '2004-11-15 16:04:06', 'id' => 86096, 'title' => 'IRC' },
{ 'id' => 16, 'created' => '2004-10-07 16:15:29', 'title' => 'About' }
);
my @a2 = (
{ 'title' => 'Legal Notice', 'created' => '2004-10-07 00:17:45', 'id' => 14 },
{ 'created' => '2004-11-15 16:xxx:06', 'id' => 86096, 'title' => 'IRC' },
{ 'id' => 16, 'created' => '2004-10-07 16:15:29', 'title' => 'About' }
);
my @only_in_two = grep {
my $e2 = $_;
none { hr_eq($e2, $_) } @a1;
} @a2;
dd \@only_in_two;
@a2
这正确识别了不存在的元素@a1
(带有xxx
时间戳)。
笔记
这会找到一个数组的哪些元素不在另一个数组中,而不是数组之间的全部差异。这是问题的具体要求。
比较依赖于数据结构(hashref)的细节;
Test::More
除非您想使用更全面的库(例如),否则无法逃避这一点。这使用字符串比较,
ne
即使是数字和时间戳。看看你的真实数据对特定元素使用更合适的比较是否有意义。在整个列表中搜索列表中的每个元素是一个O(N*M)算法。只要数据不是太大,这种(二次)复杂性的解决方案是可用的;但是,一旦数据变得足够大,以至于大小的增加会产生明显的影响,它们就会迅速分解(减慢到无用的地步)。时间来感受一下你的情况。
这里存在一种O(N+M)方法,利用哈希,如 ikegami 答案所示。一旦数据大到足以显示,这在算法上要好得多。但是,由于您的数组带有复杂的数据结构(hashrefs),因此需要做一些工作才能提出一个工作程序,特别是因为我们不知道数据。但是,如果您的数据很大,那么您肯定想要实现它。
关于过滤的一些评论。
该问题正确地观察到,对于数组的每个元素,当它在 中处理时grep
,需要检查整个其他数组。
这是在grep
使用none
from List::Util的主体中完成的。如果其块中的代码对列表的所有元素评估为 false,则返回 true;因此,如果“没有”元素满足该代码。这是要求的核心:不能在另一个数组中找到一个元素。
需要注意默认$_
变量,因为grep
和都使用它none
。
Ingrep
块$_
别名列表中当前处理的元素,因为grep
它们一一经过;我们将其保存到命名变量 ( $e2
) 中。然后none
出现并在其块中“占有” ,在处理它们时为其$_
分配元素。@a1
的当前元素@a2
也可用,因为我们已将其复制到$e2
.
执行的测试none
被拉入一个子程序,我调用hr_eq
它是为了强调它专门用于hashrefs(元素中的元素)的相等比较。
正是在这个 sub 中可以调整细节。首先,您可以为特定键添加自定义比较(数字必须使用等),而不是直截了当地使用ne
每个键的值。==
然后,如果您的数据结构发生变化,这就是您需要调整细节的地方。
推荐阅读
- daml - 在 DAML 中,如何在选择中获取 ContractId
- javascript - 在提交表单和使用快照检索数据后,防止在 Firebase 应用程序中重复邮件 ID 和 ph num 而无需身份验证
- python - 在 Python 中等待非阻塞用户输入时显示提示
- c - 将 Keras 模型转换为 C
- django - 如何在 Django 框架中使用 Jinja?
- unit-testing - Spock - 超时后成功通过测试
- python - 从元组列表中获取唯一元素的问题
- google-chrome - Not able to find elements in side a iframe using xpath
- haskell - 了解翻转功能
- java - JAXB 父子节点同名。子节点返回空值