Perl Weekly Challenge 220: Common Characters

These are some answers to the Week 220, 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 June 11, 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.

Common Characters

You are given a list of words.

Write a script to return the list of common characters (sorted alphabetically) found in every word of the given list.

Example 1

Input: @words = ("Perl", "Rust", "Raku")
Output: ("r")

Example 2

Input: @words = ("love", "live", "leave")
Output: ("e", "l", "v")

Common Characters in Raku

Raku provides operators to make this task very simple. Each word is split into a list of individual letters and then, we use the infix set intersection operator,infix%E2%88%AA) to find the common characters. Note that we use the intersection operator together with the [] reduction meta-operator, which makes it possible to apply the intersection operator to any number of input lists.

sub common-char (@in) {
    return sort keys ([∩] map {.lc.comb}, @in);
}

for <Perl Rust Raku>, <love live leave> -> @test {
    printf "%-15s => ", "@test[]";
    say common-char @test;
}

This program displays the following output:

$ raku ./common-characters.raku
Perl Rust Raku  => (r)
love live leave => (e l v)

This program is simple enough to boil down to a Raku one-liner:

$ raku -e 'say sort keys ([∩] map {.lc.comb}, @*ARGS)' love live leave
(e l v)

Common Characters in Perl

Perl doesn't have sets or set operators, so we use hashes instead. For each list of characters, we first remove any duplicate letters (using the %unique hash and then populate the %histo histogram hash. At the end, we keep any letter whose count is equal to the number of words in the initial input list.

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

sub common_char {
    my %histo;
    my @in = map lc $_, @_;
    for my $word (@in) {
        my %unique = map { $_ => 1 } split //, $word;
        $histo{$_}++ for keys %unique;
    }
    return sort grep { $histo{$_} == scalar @in } keys %histo;
}

for my $test ([<Perl Rust Raku>], [<love live leave>]) {
  printf "%-15s => ", "@$test";
  say join " ", common_char @$test;
}

This program displays the following output:

$ perl ./common-characters.pl
Perl Rust Raku  => r
love live leave => e l v

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 June 18, 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.