Nothing But Strings
with Raku

by Arne Sommer

Nothing But Strings with Raku

[259] Published 21. October 2023.

This is my response to The Weekly Challenge #239.

Challenge #239.1: Same String

You are given two arrays of strings.

Write a script to find out if the word created by concatenating the array elements is the same.

Example 1:
Input: @arr1 = ("ab", "c")
       @arr2 = ("a", "bc")
Output: true

Using @arr1, word1 => "ab" . "c" => "abc"
Using @arr2, word2 => "a" . "bc" => "abc"
Example 2:
Input: @arr1 = ("ab", "c")
       @arr2 = ("a", "bc")
Output: true

Using @arr1, word1 => "ab" . "c" => "abc"
Using @arr2, word2 => "a" . "bc" => "abc"
Example 3:
Input: @arr1 = ("ab", "c")
       @arr2 = ("ac", "b")
Output: false

Using @arr1, word1 => "ab" . "c" => "abc"
Using @arr2, word2 => "ac" . "b" => "acb"

This is easy, if we assume that the order of the array elements matter - as inferred by the examples.

#! /usr/bin/env raku

unit sub MAIN ($arr1, $arr2);             # [1]

say $arr1.words.join eq $arr2.words.join  # [2]
  ?? 'true'                               # [2a]
  !! 'false';                             # [2b]

[1] Two input strings, with space as the separator between the array elements.

[2] Get the array elements (with words), glue them together (without the spaces, with join) and compare the two resulting strings. Print 'true' if they are equal [2a], and 'false' [2b] if not.

Running it:

$ ./same-string "ab c" "a bc"
true

$ ./same-string "ab c" "ac b"
false

$ ./same-string "ab cd e" "abcde"
true

It is easyish to support random order:

File: same-string-shuffle
#! /usr/bin/env raku

unit sub MAIN ($arr1, $arr2);

for $arr1.words.permutations>>.join.unique -> $first     # [1]
{
  for $arr2.words.permutations>>.join.unique -> $second  # [2]
  {
    if $first eq $second                                 # [3]
    {
       say 'true';                                       # [3a]
       exit;                                             # [3b]
    }
  }
}

say 'false';                                             # [4]

[1] Split the first string into words, generate all the possible permutations (shuffled lists), and join each one into a single string. Remove duplicates (*), if any (with unique). Iterate over them.

[2] Ditto, for the second string.

[3] Equal? If so, say so [3a] and exit [3b].

[4] We have looked through all the permutations without finding a match. Then we have failed. Say so.

[*] Duplicates occur if we have duplicate values in the input array. E.g. "a", "a", "b".

Running it gives the expected result:

$ ./same-string-shuffle "ab c" "a bc"
true

$ ./same-string-shuffle "ab c" "ac b"
false

$ ./same-string-shuffle "ab cd e" "abcde"
true

Let us show off random order:

$ ./same-string-shuffle "c e ab d" "abcde"
true

$ ./same-string-shuffle "ce ab d" "abcde"
false

Missing verbose mode?

It is easy to add, but the duplicate removal (unique in [1] and [2]) had to go.

File: same-string-verbose
#! /usr/bin/env raku

unit sub MAIN ($arr1, $arr2, :v(:$verbose));

for $arr1.words.permutations -> @first     # [1]
{
  for $arr2.words.permutations -> @second  # [1a]
  {
    if @first.join eq @second.join         # [1b]
    {
       say ": { @first.map({ "\"$_\"" }).join(" ") } ===  \
         { @second.map({ "\"$_\"" }).join(" ") }" if $verbose;
       say 'true';
       exit;
    }
    elsif $verbose
    {
       say ": { @first.map({ "\"$_\"" }).join(" ") } =!=  \
         { @second.map({ "\"$_\"" }).join(" ") }";
    }
  }
}

say 'false';

[1] We have arrays this time in the two loops, so we must join them together (in [1b]) before we can compare the strings.

Running it:

$ ./same-string-verbose -v "ab c" "a bc"
: "ab" "c" ===  "a" "bc"
true

$ ./same-string-verbose -v "c e ab d" "abcde"
: "c" "e" "ab" "d" =!=  "abcde"
: "c" "e" "d" "ab" =!=  "abcde"
: "c" "ab" "e" "d" =!=  "abcde"
: "c" "ab" "d" "e" =!=  "abcde"
: "c" "d" "e" "ab" =!=  "abcde"
: "c" "d" "ab" "e" =!=  "abcde"
: "e" "c" "ab" "d" =!=  "abcde"
: "e" "c" "d" "ab" =!=  "abcde"
: "e" "ab" "c" "d" =!=  "abcde"
: "e" "ab" "d" "c" =!=  "abcde"
: "e" "d" "c" "ab" =!=  "abcde"
: "e" "d" "ab" "c" =!=  "abcde"
: "ab" "c" "e" "d" =!=  "abcde"
: "ab" "c" "d" "e" ===  "abcde"
true

$ ./same-string-verbose -v "ce ab d" "abcde"
: "ce" "ab" "d" =!=  "abcde"
: "ce" "d" "ab" =!=  "abcde"
: "ab" "ce" "d" =!=  "abcde"
: "ab" "d" "ce" =!=  "abcde"
: "d" "ce" "ab" =!=  "abcde"
: "d" "ab" "ce" =!=  "abcde"
false

Challenge #239.2: Consistent Strings

You are given an array of strings and allowed string having distinct characters.

You are given an array of strings and allowed string having distinct characters.

Write a script to return the number of consistent strings in the given array.

Example 1:
Input: @str = ("ad", "bd", "aaab", "baa", "badab")
       $allowed = "ab"
Output: 2

Strings "aaab" and "baa" are consistent since they only contain characters 'a' and 'b'.
Example 2:
Input: @str = ("a", "b", "c", "ab", "ac", "bc", "abc")
       $allowed = "abc"
Output: 7
Example 3:
Input: @str = ("cc", "acd", "b", "ba", "bac", "bad", "ac", "d")
       $allowed = "cad"
Output: 4

Strings "cc", "acd", "ac", and "d" are consistent.
File: consistent-string
#! /usr/bin/env raku

unit sub MAIN ($allowed, *@str where @str.elems > 0);       # [1]

my @allow-canonical = $allowed.comb;                        # [2]

my $consistent = 0;                                         # [3]

for @str -> $str                                            # [4]
{
  $consistent++ if $str.comb.unique (<=) @allow-canonical;  # [5]
}

say $consistent;                                            # [6]

[1] The allowed characters as the first argument, followed by the strings.

[2] A list of allowed characters.

[3] The final count will end up here.

[4] Iterate over the strings.

[5] Increse the counter if the characters in the string is a subset (or equal) (the (<=) operator) to the allowed characters.

See docs.raku.org/language/operators infix (<=) for more information about the subset or equal to operator (<=).

[6] Print the result.

Running it:

$ ./consistent-string ab ad bd aaab baa badab
2

$ ./consistent-string abc a b c ab ac bc abc 
7

$ ./consistent-string cad cc acd b ba bac bad ac d
4

Looking good.

Missing verbose mode? Let us remedy that.

File: consistent-string-verbose
#! /usr/bin/env raku

unit sub MAIN ($allowed, *@str where @str.elems > 0, :v(:$verbose));

my @allow-canonical = $allowed.comb;

my $consistent = 0;

for @str -> $str
{
  my @str-canonical = $str.comb.unique;

  if @str-canonical (<=) @allow-canonical
  {
    $consistent++;
    say ": + $str - consistent" if $verbose;
  }
  elsif $verbose
  {
    say ": - $str" if $verbose;

  }
}

say $consistent;

Running it:

$ ./consistent-string-verbose -v ab ad bd aaab baa badab
: - ad
: - bd
: + aaab - consistent
: + baa - consistent
: - badab
2

$ ./consistent-string-verbose -v abc a b c ab ac bc abc 
: + a - consistent
: + b - consistent
: + c - consistent
: + ab - consistent
: + ac - consistent
: + bc - consistent
: + abc - consistent
7

$ ./consistent-string-verbose -v cad cc acd b ba bac bad ac d
: + cc - consistent
: + acd - consistent
: - b
: - ba
: - bac
: - bad
: + ac - consistent
: + d - consistent
4

And that's it.