Perl Weekly Challenge: Week 233
Challenge 1:
Similiar Words
You are given an array of words made up of alphabets only.
Write a script to find the number of pairs of similar words. Two words are similar if they consist of the same characters.
Example 1
Input: @words = ("aba", "aabb", "abcd", "bac", "aabc")
Output: 2
Pair 1: similar words ("aba", "aabb")
Pair 2: similar words ("bac", "aabc")
Example 2
Input: @words = ("aabb", "ab", "ba")
Output: 3
Pair 1: similar words ("aabb", "ab")
Pair 2: similar words ("aabb", "ba")
Pair 3: similar words ("ab", "ba")
Example 3
Input: @words = ("nba", "cba", "dba")
Output: 0
We can just about manage this one as a one-liner.
@*ARGS.map({$_.comb.unique.sort.join}).combinations(2).grep({@$_[0]eq@$_[1]}).elems.say
We take the command-line arguments (from @*ARGS
) and transform them with .map()
using .comb()
to split each one into individual
characters, find the unique ones with .unique()
, sort them in alphabetical order with (.sort()
) and join them back up again with
.join()
.
Next we form all the pairs of elements of that transformed array with .combinations(2)
and then use .grep()
to filter out
those pairs whose constituents are the same as each other. .elems()
counts these matching pairs and .say()
prints the result.
The core of the Perl version is also quite concise but it needs some extra help. I have unique()
and combinations()
functions
from previous challenges that I used to emulate things Raku has but are missing from Perl's standard library.
say scalar grep { $_->[0] eq $_->[1] }
combinations([ map { join q{}, sort (unique([ split //, $_ ])); } @ARGV], 2);
Challenge 2:
Frequency Sort
You are given an array of integers.
Write a script to sort the given array in increasing order based on the frequency of the values. If multiple values have the same frequency then sort them in decreasing order.
Example 1
Input: @ints = (1,1,2,2,2,3)
Ouput: (3,1,1,2,2,2)
'3' has a frequency of 1
'1' has a frequency of 2
'2' has a frequency of 3
Example 2
Input: @ints = (2,3,1,3,2)
Ouput: (1,3,3,2,2)
'2' and '3' both have a frequency of 2, so they are sorted in decreasing order.
Example 3
Input: @ints = (-1,1,-6,4,5,-6,1,4,1)
Ouput: (5,-1,4,4,-6,-6,1,1,1)
Raku has a method called .classify()
which is quite handy and even in its most basic usage is helpful for this task.
The line below creates a hash whose keys are unique integers in @ints
and whose values are a list of each occurrence
of that integer (i.e. its' frequency).
my %freq = @ints.classify({$_});
Then, (ignoring say()
for a minute)...
say q{(},
...we take the keys of %freq
...
%freq
.keys
... and sort them in ascending order according to the size of their value lists using .elems()
.
In the event there are keys with the same frequency (they are not lesser or greater,) we
sort again in descending numeric order of the keys.
.sort({ %freq{$^a}.elems <=> %freq{$^b}.elems || $^b <=> $^a })
Then we replace the keys with their values taking care to make one "flat" array with |
...
.map({ | %freq{$_} })
...and join the elements with commas. The say()
at the top is to surround the result with parentheses to make
it look like the output in the examples.
.join(q{,}),
q{)};
This is the perl version. I had to provide my own classify()
function but other than that Perl has everything needed.
my %freq = classify(@ARGV);
say q{(},
(
join q{,},
map { @{$freq{$_}} }
sort { scalar @{$freq{$a}} <=> scalar @{$freq{$b}} || $b <=> $a }
keys %freq
),
q{)};