Perl-比较两个嵌套的哈希 [英] Perl - Compare two nested hash

查看:186
本文介绍了Perl-比较两个嵌套的哈希的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的情况,其中从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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆