This is my response to The Weekly Challenge #265.
@ints
.
33%
or more. If more than one found, return the smallest. If none found then
return undef.
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
$str
containing alphnumeric characters and
array of strings (alphabetic characters only), @str
.
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.