2 votes

Perl - Comparer deux hachages imbriqués

Voici mon scénario, où il y a 2 hachages qui ont été décodés à partir de 2 fichiers JSON.

J'ai 2 hachages complexes,

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

Je veux comparer ces 2 hachages pour l'égalité, et j'ai utilisé Compare de Data::Compare et is_deeply de Test::More. Les deux ne tiennent pas compte de l'ordre du tableau.
Je veux comparer en ignorant l'ordre des valeurs du tableau de la clé 'k21'.
Mon application remplit le tableau à partir de 'keys %hash' qui donne un ordre aléatoire.
J'ai essayé 'ignore_hash_keys' de Data::Compare, mais mon hachage peut parfois être complexe et je ne veux pas l'ignorer.

La clé "k21" peut aussi parfois contenir un tableau de hachages.

$hash3 = {k1=> { k11 => v1}, k2 => { k21 => [{v3 => v31}, {v2 => v22}] }}

Comment puis-je comparer des hachages aussi complexes en ignorant l'ordre des tableaux.

5voto

simbabque Points 11124

Vous pouvez utiliser Test::Deep qui prévoit cmp_deeply . Il est beaucoup plus polyvalent que la fonction de 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, );

L'astuce est la suivante bag() fonction qui ignore l'ordre des éléments.

Elle effectue une comparaison de sac, c'est-à-dire qu'elle compare deux tableaux mais ignore l'ordre des éléments [...].


Mise à jour : A partir de votre commentaire :

Comment mettre en sac toutes les références de tableaux dans un hash de façon dynamique ?

Quelques recherches dans le code de Test::Deep ont montré qu'il est possible de l'écraser. J'ai regardé at Test::Deep lui-même d'abord, et a constaté qu'il y a un Test::Deep::Array qui traite des tableaux. Tous les paquets qui manipulent des choses à l'intérieur de T::D ont a descend méthode . C'est donc là qu'il faut s'accrocher.

Sub::Override est idéal pour remplacer temporairement des éléments, au lieu de s'embêter avec des blocs de type.

En fait, tout ce que nous devons faire est de remplacer l'appel à Test::Deep::arrayelementsonly en Test::Deep::Array::descend La dernière ligne de l'article est un appel à bag() . Le reste est simplement copié (l'indentation est de moi). Pour les petits singe-Parcheando une copie du code existant avec une légère modification est généralement l'approche la plus simple.

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 );

Cela fera passer le test.

Assurez-vous de réinitialiser l'annulation en indéfinissant $sub ou le laisser sortir de son champ d'application, ou vous pourriez avoir de drôles de surprises si le reste de votre suite de tests utilise également Test::Deep.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X