Perl-比较两个嵌套的哈希 [英] Perl - Compare two nested hash
问题描述
这是我的情况,其中从2个JSON文件解码了2个散列.
This is my scenario, where there are 2 hashes that have been decoded from 2 JSON files.
我有2个复杂的哈希,
$hash1 = {k1=> { k11 => v1, k12 => v2}, k2 => { k21 => [v1, v2, v3] }}
$hash2 = {k1=> { k11 => v1, k12 => v2}, k2 => { k21 => [v3, v2, v1] }}
我想比较这两个散列是否相等,并使用Compare :: Compare和Test :: More的is_deeply.两者都不会忽略数组的顺序.
我想比较忽略键'k21'的数组值的顺序.
我的应用程序从给出随机顺序的键%hash"填充数组.
尝试过Data :: Compare的'ignore_hash_keys',但是我的哈希有时会很复杂,并且不想忽略.
I want to compare these 2 hash for equality, and used Compare of Data::Compare and is_deeply of Test::More. Both do not ignore the order of the array.
I want to compare ignoring the order of array values of key 'k21'.
My App populates the array from 'keys %hash' which gives random order.
Tried 'ignore_hash_keys' of Data::Compare, but my hash sometimes can be complex and don't want to ignore.
键"k21"有时也可以具有哈希数组.
Key 'k21' can also sometimes have array of hashes.
$hash3 = {k1=> { k11 => v1}, k2 => { k21 => [{v3 => v31}, {v2 => v22}] }}
如何通过忽略数组顺序来比较复杂的哈希值.
How do I compare such complex hash by ignoring the array order.
推荐答案
您可以使用测试:: Deep (提供cmp_deeply
).它比Test :: More的is_deeply
更具通用性.
You can use Test::Deep, which provides cmp_deeply
. It's a lot more versatile than Test::More's is_deeply
.
use Test::Deep;
my $hash1 = {
k1 => { k11 => 'v1', k12 => 'v2' }, k2 => { k21 => [ 'v1', 'v2', 'v3' ] } };
my $hash2 = {
k1 => { k11 => 'v1', k12 => 'v2' }, k2 => { k21 => bag( 'v3', 'v2', 'v1' ) } };
cmp_deeply( $hash1, $hash2, );
诀窍是 bag()
函数,该函数忽略了元素顺序.
The trick is the bag()
function, which ignores the order of elements.
这是一个袋比较,也就是说,它比较两个数组,但是忽略了元素的顺序[...]
This does a bag comparison, that is, it compares two arrays but ignores the order of the elements [...]
更新:来自您的评论:
Update: From your comment:
我如何将所有数组引用动态地放入散列中
How do I bag all array references inside hash dynamically
一些对Test :: Deep代码的挖掘表明,有可能将其覆盖.我在Test :: Deep中查看了 本身,然后发现有一个 Test :: Deep :: Array ,它处理数组.所有处理T :: D内部内容的软件包都具有 a descend
方法.这就是我们需要连接的地方.
Some digging in the code of Test::Deep showed that it's possible to overwrite it. I looked at Test::Deep itself first, and found that there is a Test::Deep::Array, which deals with arrays. All of the packages that handle stuff inside of T::D have a descend
method. So that's where we need to hook in.
Sub :: Override 非常适合临时重写内容,而不是与typeglob混淆.
Sub::Override is great to temporarily override stuff, instead of messing with typeglobs.
基本上,我们要做的就是用对bag()
的调用替换对Test::Deep::Array::descend
最后一行中对Test::Deep::arrayelementsonly
的调用.其余的仅被复制(缩进是我的).对于较小的 monkey-patching ,对现有代码进行稍作修改通常是最简单的方法
Basically all we need to do is replace the call to Test::Deep::arrayelementsonly
in Test::Deep::Array::descend
's final line with a call to bag()
. The rest is simply copied (indentation is mine). For small monkey-patching a copy of the existing code with a slight modification is usually the easiest approach.
use Test::Deep;
use Test::Deep::Array;
use Sub::Override;
my $sub = Sub::Override->new(
'Test::Deep::Array::descend' => sub {
my $self = shift;
my $got = shift;
my $exp = $self->{val};
return 0 unless Test::Deep::descend(
$got, Test::Deep::arraylength( scalar @$exp ) );
return 0 unless $self->test_class($got);
return Test::Deep::descend( $got, Test::Deep::bag(@$exp) );
}
);
my $hash1 = {
k1 => { k11 => 'v1', k12 => 'v2' },
k2 => { k21 => [ 'v1', 'v2', 'v3' ] }
};
my $hash2 = {
k1 => { k11 => 'v1', k12 => 'v2' },
k2 => { k21 => [ 'v3', 'v2', 'v1' ] }
};
cmp_deeply( $hash1, $hash2 );
这将使测试通过.
请确保通过取消定义$sub
或使其超出范围来重置替代,否则,如果测试套件的其余部分也使用Test :: Deep,您可能会感到奇怪.
Make sure to reset the override by undefining $sub
or letting it go out of scope, or you might have some weird surprises if the rest of your test suite also uses Test::Deep.
这篇关于Perl-比较两个嵌套的哈希的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!