by Arne Sommer

# 33% Word with Raku

[285] Published 18. April 2024.

This is my response to The Weekly Challenge #265.

## Challenge #265.1: 33% Appearance

You are given an array of integers, `@ints`.

Write a script to find an integer in the given array that appeared `33%` or more. If more than one found, return the smallest. If none found then return undef.

Example 1: ```Input: @ints = (1,2,3,3,3,3,4,2) Output: 3 1 appeared 1 times. 2 appeared 2 times. 3 appeared 4 times. 3 appeared 50% (>33%) in the given array. ``` Example 2: ```Input: @ints = (1,1) Output: 1 1 appeared 2 times. 1 appeared 100% (>33%) in the given array. ``` Example 3: ```Input: @ints = (1,2,3) Output: 1 1 appeared 1 times. 2 appeared 1 times. 3 appeared 1 times. Since all three appeared 33.3% (>33%) in the given array. We pick the smallest of all. ```

File: appearance ```#! /usr/bin/env raku unit sub MAIN (*@ints where all(@ints) ~~ Int && @ints.elems > 0, # [1] :v(:\$verbose)); my \$limit = @ints.elems * 0.33; # [2] my %freq = @ints.Bag; # [3] my %ok = %freq.grep({ \$_.value >= \$limit }); # [4] my @keys = %ok.keys.sort; # [5] if \$verbose { say ": Frequencies: { %freq.raku }"; say ": Limit: \$limit"; say ": OK: { %ok.raku }"; say ": Sorted: { @keys.join(",") }"; } say @keys.first; # [6] ```

[1] Ensure a lot of integers, at least one.

[2] The limit, as a real number instead of as a percent of the total.

[3] Get the frequency of the digits, with `Bag`. The assignment to a `%`-sigilled variable coerces that Bag into a hash.

See docs.raku.org/routine/Bag for more information about `Bag`.

[4] Use `grep` to retain only the values with a high enough frequency.

[5] Get the values themselves, in sorted order.

[6] Print the `first` value in the array (of values). This will give an undefined value if the array is empty, which is fine.

See docs.raku.org/routine/first for more information about `first`.

Running it:

```\$ ./appearance 1 2 3 3 3 3 4 2 5 6 7 8 9 Nil \$ ./appearance 1 1 1 \$ ./appearance 1 2 3 3 ```

Looking good(ish).

If you do not like the Raku `Nil`, but want an explicit `undef` (which in Raku we must specify as a string, as it is not a thing - so to speak), swap row [6] with something like this:

```say @keys ?? @keys.first !! 'undef'; ```

With verbose mode:

```\$ ./appearance -v 1 2 3 3 3 3 4 2 5 6 7 8 9 : Frequencies: {"1" => 1, "2" => 2, "3" => 4, "4" => 1, "5" => 1, "6" => 1, "7" => 1, "8" => 1, "9" => 1} : Limit: 4.29 : OK: {} : Sorted: Nil \$ ./appearance -v 1 1 : Frequencies: {"1" => 2} : Limit: 0.66 : OK: {"1" => 2} : Sorted: 1 1 \$ ./appearance -v 1 2 3 : Frequencies: {"1" => 1, "2" => 1, "3" => 1} : Limit: 0.99 : OK: {"1" => 1, "2" => 1, "3" => 1} : Sorted: 1,2,3 3 ```

## Challenge #265.2: Completing Word

You are given a string, `\$str` containing alphnumeric characters and array of strings (alphabetic characters only), `@str`.

Write a script to find the shortest completing word. If none found return empty string.

A completing word is a word that contains all the letters in the given string, ignoring space and number. If a letter appeared more than once in the given string then it must appear the same number or more in the word.

Example 1: ```Input: \$str = 'aBc 11c' @str = ('accbbb', 'abc', 'abbc') Output: 'accbbb' The given string contains following, ignoring case and number: a 1 times b 1 times c 2 times The only string in the given array that satisfies the condition is 'accbbb'. ``` Example 2: ```Input: \$str = 'Da2 abc' @str = ('abcm', 'baacd', 'abaadc') Output: 'baacd' The given string contains following, ignoring case and number: a 2 times b 1 times c 1 times d 1 times The are 2 strings in the given array that satisfies the condition: 'baacd' and 'abaadc'. Shortest of the two is 'baacd' ``` Example 3: ```Input: \$str = 'JB 007' @str = ('jj', 'bb', 'bjb') Output: 'bjb' The given string contains following, ignoring case and number: j 1 times b 1 times The only string in the given array that satisfies the condition is 'bjb'. ```

File: completing-word ```#! /usr/bin/env raku unit sub MAIN (\$str, *@str, :v(:\$verbose)); # [1] my \$str-b = \$str.comb.grep( * ~~ /<[a..z A..Z]>/)>>.lc.Bag; # [2] say ": Str Bag: { \$str-b.raku }" if \$verbose; for @str.sort: *.chars -> \$candidate # [3] { my \$candidate-bag = \$candidate.comb>>.lc.Bag; # [4] say ": Checking \$candidate (Bag: { \$candidate-bag.raku })" if \$verbose; if \$str-b (<=) \$candidate-bag # [5] { say \$candidate; # [5a] exit; # [5b] } } ```

[1] First the string, followed by the array of strings. I have chosen not to add content checks this time.

[2] Extract all the letters (the `grep`) from the individual characters (`comb`), convert each one to lowercase (`>>.lc`) and turn the result into a `Bag`.

[3] We are looking for the shortest match, so we iterate over the candidates by string length, shortest first.

[4] Turn the candidate into a `Bag`, as done with the string (in [2]).

[5] Do we have a subset (or equal) set of characters (the `(<=)` operator) to the allowed characters? If so, print the matching candidate and exit.

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

Running it:

```\$ ./completing-word "aBc 11c" accbbb abc abbc accbbb \$ ./completing-word "Da2 abc" abcm baacd abaadc baacd \$ ./completing-word "JB 007" jj bb bjb bjb ```

Looking good.

With verbose mode:

```\$ ./completing-word -v "aBc 11c" accbbb abc abbc : Str Bag: ("a"=>1,"c"=>2,"b"=>1).Bag : Checking abc (Bag: ("a"=>1,"b"=>1,"c"=>1).Bag) : Checking abbc (Bag: ("c"=>1,"b"=>2,"a"=>1).Bag) : Checking accbbb (Bag: ("c"=>2,"a"=>1,"b"=>3).Bag) accbbb \$ ./completing-word -v "Da2 abc" abcm baacd abaadc : Str Bag: ("b"=>1,"c"=>1,"a"=>2,"d"=>1).Bag : Checking abcm (Bag: ("c"=>1,"a"=>1,"m"=>1,"b"=>1).Bag) : Checking baacd (Bag: ("a"=>2,"d"=>1,"c"=>1,"b"=>1).Bag) baacd \$ ./completing-word -v "JB 007" jj bb bjb : Str Bag: ("b"=>1,"j"=>1).Bag : Checking jj (Bag: ("j"=>2).Bag) : Checking bb (Bag: ("b"=>2).Bag) : Checking bjb (Bag: ("b"=>2,"j"=>1).Bag) bjb ```

And that's it.