Perl Weekly Challenge 242: Missing Members

These are some answers to the Week 242, Task 1, of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Spoiler Alert: This weekly challenge deadline is due in a few days from now (on November 12, 2023, at 23:59). This blog post offers some solutions to this challenge. Please don’t read on if you intend to complete the challenge on your own.

Task 1: Missing Members

You are given two arrays of integers.

Write a script to find out the missing members in each other arrays.

Example 1

Input: @arr1 = (1, 2, 3)
       @arr2 = (2, 4, 6)
Output: ([1, 3], [4, 6])

(1, 2, 3) has 2 members (1, 3) missing in the array (2, 4, 6).
(2, 4, 6) has 2 members (4, 6) missing in the array (1, 2, 3).

Example 2

Input: @arr1 = (1, 2, 3, 3)
       @arr2 = (1, 1, 2, 2)
Output: ([3])

(1, 2, 3, 3) has 2 members (3, 3) missing in the array (1, 1, 2, 2). Since they are same, keep just one.
(1, 1, 2, 2) has 0 member missing in the array (1, 2, 3, 3).

Note that I consider the output of example 2 slightly wrong, as I believe it should be made clearer that we get an empty array. So, I think it should rather be something like ([3], []).

Missing Members in Raku

Here we use the set difference operator,infix%E2%88%96). Note that when its arguments are simple arrays or lists, they are implicitly converted to sets prior to the execution of the set difference operator. We don't need explicit conversion. This leads to a very simple one-line solution.

sub diff (@a, @b) {
    return map {.keys}, @a (-) @b, @b (-) @a;
}

for (<1 2 3>, < 2 4 6>), (<1 2 3 3>, <1 1 2 2>) -> @test {
    printf "%-8s - %-8s => ", "@test[0]", "@test[1]";
    say diff @test[0], @test[1];
}

This program displays the following output:

$ raku ./missing-members.raku
1 2 3    - 2 4 6    => ((3 1) (4 6))
1 2 3 3  - 1 1 2 2  => ((3) ())

Missing Members in Perl

The Perl solution is quite different. Since Perl has no sets (and therefore no set difference operator), we'll use hashes to find manually items that are in one array and not in the other one, leading so a significantly longer, but not really more complicated, solution. What makes to Perl solution slightly more complex is the need to pass around references to arrays, rather than simple arrays, to avoid the array flattening feature of Perl. We simplify sightly this by returning solutions as ready-to-be-printed stings, rather than Perl data structures.

use strict;
use warnings;
use feature 'say';

sub diff {
    my ($aref, $bref) = @_;
    my %a = map {$_ => 1} @$aref;
    my %b = map {$_ => 1} @$bref;
    my $adif = [grep { not exists $b{$_} } keys %a];
    my $bdif = [grep { not exists $a{$_} } keys %b];
    return "(@$adif) (@$bdif)";
}

my @tests = ( [[<1 2 3>],   [<2 4 6>]], 
              [[<1 2 3 3>], [<1 1 2 2>]]
            );
for my $test (@tests) {
    printf "%-10s - %-10s => ", 
           "@{$test->[0]}", "@{$test->[1]}";
    say diff ($test->[0], $test->[1]);
}

This program displays the following output:

$ perl ./missing-members.pl
1 2 3      - 2 4 6      => (1 3) (4 6)
1 2 3 3    - 1 1 2 2    => (3) ()

Wrapping up

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on November 19, 2023. And, please, also spread the word about the Perl Weekly Challenge if you can.

Leave a comment

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.