这是我的情况,其中从2个JSON文件解码了2个散列。

我有2个复杂的哈希,

$hash1 = {k1=> { k11 => v1, k12 => v2}, k2 => { k21 => [v1, v2, v3] }}
$hash2 = {k1=> { k11 => v1, k12 => v2}, k2 => { k21 => [v3, v2, v1] }}

我想比较这两个哈希是否相等,并使用数据比较:: Compare和Test_:More的is_deeply。两者都不忽略数组的顺序。
我想比较忽略键“k21”的数组值的顺序。
我的应用程序根据给出随机顺序的“键%hash”填充数组。
尝试过Data::Compare的'ignore_hash_keys',但是我的哈希有时会很复杂,并且不想忽略。

键“k21”有时也可以具有哈希数组。
$hash3 = {k1=> { k11 => v1}, k2 => { k21 => [{v3 => v31}, {v2 => v22}] }}

如何通过忽略数组顺序来比较复杂的哈希。

最佳答案

您可以使用Test::Deep,它提供cmp_deeply。它比Test::More的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() function,它忽略元素的顺序。



更新:来自your comment:



对Test::Deep的代码进行的一些挖掘表明,可以覆盖它。我首先看了at Test::Deep本身,发现有一个Test::Deep::Array,它处理数组。所有处理T::D内部内容的软件包都具有a descend method。这就是我们需要了解的地方。

Sub::Override非常适合临时覆盖内容,而不是与typeglob混淆。

基本上,我们要做的就是用对Test::Deep::arrayelementsonly的调用替换Test::Deep::Array::descend最后一行中对bag()的调用。其余的仅被复制(缩进是我的)。对于较小的monkey-patching,最简单的方法通常是对现有代码进行少量修改。
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,您可能会感到奇怪。

10-07 15:34