Perl Weekly Challenge: Week 260

Challenge 1:

Unique Occurrences

You are given an array of integers, @ints.

Write a script to return 1 if the number of occurrences of each value in the given array is unique or 0 otherwise.

Example 1
Input: @ints = (1,2,2,1,1,3)
Output: 1

The number 1 occurred 3 times.
The number 2 occurred 2 times.
The number 3 occurred 1 time.

All occurrences are unique, therefore the output is 1.
Example 2
Input: @ints = (1,2,3)
Output: 0
Example 3
Input: @ints = (-2,0,1,-2,1,1,0,1,-2,9)
Output: 1

In my Raku solution, the first thing I did was create a hash to store how many times each value was seen.

my %count;

Then I did the actual counting.{ %count{$_}++ });

If the total number of values seen is equal to the unique number of values seen, we print 1 otherwise we print 0.

say %count.values.elems == %count.values.unique.elems ?? 1 !! 0;

(Full code on Github.)

For the Perl version I once again had to reach into my archive of previous challenges; this time to find a replacement for .unique(). With that, it looks pretty similar to the Raku version.

my %count;
map { $count{$_}++ } @ints;
say scalar values %count == scalar unique([values %count]) ? 1 : 0;

(Full code on Github.)

Challenge 2:

Dictionary Rank

You are given a word, $word.

Write a script to compute the dictionary rank of the given word.

Example 1
Input: $word = 'CAT'
Output: 3

All possible combinations of the letters:

Arrange them in alphabetical order:

CAT is the 3rd in the list.
Therefore the dictionary rank of CAT is 3.
Example 2
Input: $word = 'GOOGLE'
Output: 88
Example 3
Input: $word = 'SECRET'
Output: 255

Raku can solve this in one slightly long line.

say %(@*ARGS[0]{$_.join}).sort.unique.kv.reverse){@*ARGS[0]} + 1

(Full code on Github.)

Starting from somewhere near the beginning, we get the word from the command line and split it into a list of characters with .comb(). Then we find all the permutations of that list with .permutations(). We want strings not list so we join all those permutation lists back with .map() and .join(). These strings are then sorted into dictionary order with .sort() and duplicates are filtered out with .unique(). At this point, we have a list of strings. .kv() converts a list or array into key-value pairs where the value is an element of the list and the key is the index of the element in the list. .Hash casts this list of pairs into a hash. But this hash is backwards for our purposes; we want to be able to look up the index based on the word not the other way around so we call .reverse() which makes keys values and values keys. I wrapped up this whole statement in %( ... ). This treats the key-value list as a hash. Now we can use the standard hash {} syntax to look up the index of the word. One more slight problem though; array indexing starts from 0 but according to the spec, the rank should be 1-based. So we have to add 1 to get the final answer.

For perl we need a unique() replacement as in Challenge 1 and the permute() function I have used in previous challenges.

my @permutations;
permute { push @permutations, \@_; } (split //, $word);

I need an explicit hash to store the dictionary ranks.

my %rank;

And then I can populate the hash with my permuted strings. The keys are the strings themselves, and the values are their index in the list of permutations.

my $index = 1;
for my $perm (sort (unique([map { join q{}, @{$_} } @permutations]))) {
    $rank{$perm} = $index++;

Finally, I can look up the rank of the word and print it.

say $rank{$word};

(Full code on Github.)