Perl Weekly Challenge: Week 284
Challenge 1:
Lucky Integer
You are given an array of integers,
@ints
.Write a script to find the lucky integer if found otherwise return
-1
. If there are more than one then return the largest.A lucky integer is an integer that has a frequency in the array equal to its value.
Example 1
Input: @ints = (2, 2, 3, 4)
Output: 2
Example 2
Input: @ints = (1, 2, 2, 3, 3, 3)
Output: 3
Example 3
Input: @ints = (1, 1, 1, 3)
Output: -1
It has now almost become a tradition that I solve the first task of the week as a Raku one-liner. This week is no exception.
my %h = @*ARGS.classify({$_}); say (%h.keys.grep({ $_ == %h{$_}.elems }).sort)[*-1] // -1
We take the input as a series of command-line arguments. They are converted, using classify()
into a hash whose keys are unique numbers in the input and the values are each instance of their occurrence. I thought that I could
save a few characters by using the default hash %_
but it clashed with the use of $_
later on so instead I assigned it to a new variable called %h
. Next, we .grep()
through the keys of %h
(with .keys()
) and find the one with an equal number of values. This would be enough in most cases but the spec also says that in the event of more than one answer, we should return the largest. So we .sort()
the results, wrapping the whole statement
we have so far in parentheses to make the results a list and take the last element of the list ([*-1]
) The last thing we need to do is output -1 if no lucky integers were found. This is done with the defined-or operator //
.
Whatever answer is found, it is output with say()
.
The Perl, as so often happens, is a bit longer due to a lack of features that are standard in Raku.
These lines emulate the behaviour of Rakus' .classify()
with one difference; the values of the created hash keys
are the count of occurrences of integers in the input not the integers themselves.
my %count;
for my $int (@ints) {
$count{$int}++;
}
The rest of the code is a straight copy of the equivalent Raku. It can also be contained in one line.
say [sort { $b <=> $a} grep { $_ == $count{$_} } keys %count]->[0] // -1;
Challenge 2:
Relative Sort
You are given two lists of integers,
@list1
and@list2
. The elements in the@list2
are distinct and also in the@list1
.Write a script to sort the elements in the
@list1
such that the relative order of items in@list1
is same as in the@list2
. Elements that is missing in@list2
should be placed at the end of@list1
in ascending order.
Example 1
Input: @list1 = (2, 3, 9, 3, 1, 4, 6, 7, 2, 8, 5)
@list2 = (2, 1, 4, 3, 5, 6)
Output: (2, 2, 1, 4, 3, 3, 5, 6, 7, 8, 9)
Example 2
Input: @list1 = (3, 3, 4, 6, 2, 4, 2, 1, 3)
@list2 = (1, 3, 2)
Output: (1, 3, 3, 3, 2, 2, 4, 4, 6)
Example 3
Input: @list1 = (3, 0, 5, 0, 2, 1, 4, 1, 1)
@list2 = (1, 0, 3, 2)
Output: (1, 1, 1, 0, 0, 3, 2, 4, 5)
The input is taken as two command-line parameters which are strings consisting of numbers separated by spaces.
They are converted into List
s using .words()
.
my @list1 = $l1.words;
my @list2 = $l2.words;
The naive algorithm would be to iterate through @list2
and for each number, find the elements in @list1
that match it, and then add them to a list of results. However, this would require multiple traversals through @list1
so would be very inefficient.
We can do better by transforming @list1
into a Hash
where the keys are unique numbers and the values are the instances of those numbers. This is the perfect job for .classify()
.
my %elems = @list1.classify({ $_ });
We create storage for the results.
my @result;
Now we iterate through @list2
and for each number we find the appropriate key from the %elems
hash.
The values associated with that key are added to the @results
list making sure to "flatten" them with |
so
we get individual values instead of a list reference.
for @list2 -> $e {
@result.push(| %elems{$e});
We then delete that key from the hash.
%elems{$e}:delete;
}
At this point all we have left in %elems
are the keys that do not match elements of @list2
. These
keys are sorted into ascending numeric order and their values are added to @results
in a similar fashion to the loop above.
for %elems.keys.sort -> $e {
@result.push(| %elems{$e});
%elems{$e}:delete;
}
Finally the results are printed out in the same style as in the spec.
say q{(}, @result.join(q{, }), q{)};
}
This is the Perl version.
my @list1 = split /\s*/, $ARGV[0];
my @list2 = split /\s*/, $ARGV[1];
As in the previous task, we need to work around the lack of .classify()
.
my %elems;
for my $int (@list1) {
push @{$elems{$int}}, $int;
}
The rest of the code works the same as in the Raku version.
my @result;
for my $e (@list2) {
push @result, @{$elems{$e}};
delete $elems{$e};
}
for my $e (sort { $a <=> $b } keys %elems) {
push @result, @{$elems{$e}};
delete $elems{$e};
}
say q{(}, (join q{, }, @result), q{)};