I was thinking about the cross-product operator the other day,
and I was wondering if there might be a convenient way of
filtering the resulting cartesian product to do something like a
database inner join:

    my @level  = ( godzilla => 9 ,    gremlin => 3,     hanuman => 5 );
    my @origin = ( godzilla => 'jp',  tingler => 'us',  hanuman => 'il' );

    my @results = ( @level X @origin ).grep({ $_[0].keys eq $_[1].keys });
    say @results;  # ((godzilla => 6 godzilla => jp) (hanuman => 5
hanuman => il))

That's easy enough, though the resulting data structure isn't very neat.
I started looking for ways to rearrange it:

    my %joined;
    for @results -> $row {
        say "row: ", $row;          # e.g. row: (godzilla => 9 godzilla => jp)
        say $row.map({ .keys });    # e.g. ((godzilla) (godzilla))
        say $row.map({ .values });  # e.g. ((9) (jp))

        my $monster =| $row[0].keys;              # e.g. godzilla
        my @attributes =| $row.map({ .values });  # e.g. [9 jp]
        %joined{ $monster } = @attributes;
    }
    say %joined;  # {godzilla => [9 jp], hanuman => [5 il]}

I can do it more compactly, but it risks getting unreadable:

    my %joined2 =| @results.map({ $_[0].keys => .map({ .values }).flat  });

In any case, the %joined structure feels more perlish, for
example it's easier to use it to generate reports:

    for %joined.keys -> $key {
        printf "%12s: level: %-2d origin: %3s\n", $key, %joined{ $key }.flat;
    }
    #     hanuman: level: 5  origin:  il
    #    godzilla: level: 9  origin:  jp

Is there some neater way of doing this that I'm missing?

Reply via email to