Hi all,

I have a method which returns two arrayrefs: one is an array of
hashes, and the other an array of arrays. I'm writing a test harness
for this method, so I put together some testcases and expected
results. I don't care what order the arrays are in; I only care that
the arrayrefs returned by the function contains exactly the same set
of values as the expected results.

Something like this:

# embeddings is an array of hashes, $anchors is an array of arrays
my ($embeddings, $anchors) = $embedder->embed($subgraph);
my $expected => {
    embeddings => [
                {
                    a => '1',
                    b => '2',
                },
                {
                    a => '1',
                    b => '3',
                },
                {
                    a => '2',
                    b => '3',
                },
            ],
    anchors => [
                ['1','2'],
                ['2','3'],
                ['1','3'],
            ],
},

use Test::More tests => 2;
is_deeply($embeddings, $expected->{embeddings},
         "embeddings match");
is_deeply($anchors,    $expected->{anchors},
         "anchors match");

...except that I don't care if the actual and expected array referents
are in a different order, only that they contain the same items.

My first thought is to sort the arrays according to some arbitrary
convention and use is_deeply() as above; but I'm stuck as to how to
write a sort ordering well. I'm thinking I should sort the array of
hashes by lexicographical ordering of hash values, with
lexicographical significance determined by sorted key order, and sort
the array of arrays by simple lexicographical order. I have something
like this:

use List::MoreUtils qw(pairwise);

# lexicographical comparison function for lists of hashes
sub lexhash {
    my @seq_a = map {$a->{$_}} sort keys %$a;
    my @seq_b = map {$b->{$_}} sort keys %$b;
    for my $pair (pairwise {[$a,$b]} @seq_a, @seq_b) {
        return $pair->[0] cmp $pair->[1] if $pair->[0] cmp $pair->[1];
    }
    return 0;
}

# lexicographical comparison function for lists of arrays
sub lexarray {
    for my $pair (pairwise {[$a,$b]} @$a, @$b) {
        return $pair->[0] cmp $pair->[1] if $pair->[0] cmp $pair->[1];
    }
    return 0;
}

is_deeply([sort lexhash @$embeddings], [sort lexhash
@{$expected->{embeddings}}],
         "embeddings match");
is_deeply([sort lexarray @$anchors],   [sort lexarray @{$expected->{anchors}}],
         "anchors match");

This works and does what I want, but it feels hacky and I'm not
entirely happy about it. I have these questions:

1. Is there a "set" type which holds aggregate data and doesn't care
about order, which I could use to compare these results for equality?
2. If not, is my solution to the problem a reasonable design? If not,
what better way could I do it?
3. If it's a reasonable design, is there any way my implementation of
it could be improved? In particular, could it be made more readable?
I'm not convinced that I could work out what this code does next week,
let alone in six months time.

Phil

-- 
To unsubscribe, e-mail: beginners-unsubscr...@perl.org
For additional commands, e-mail: beginners-h...@perl.org
http://learn.perl.org/


Reply via email to