Perl Weekly Challenge: Week 226

Challenge 1:

Shuffle String

You are given a string and an array of indices of same length as string.

Write a script to return the string after re-arranging the indices in the correct order.

Example 1
Input: $string = 'lacelengh', @indices = (3,2,0,5,4,8,6,7,1)
Output: 'challenge'
Example 2
Input: $string = 'rulepark', @indices = (4,7,3,1,0,5,2,6)
Output: 'perlraku'

Alas, in a break with long-standing tradition, challenge 1 will not be solved with a one-liner this week. But this solution is also pretty straightforward.

First we split the $string up into a list of individual letters.

my @letters = $string.comb;

We will also need a list to store the result.

my @result;

Using the Z or zip operator, we form pairs of a letter from @letters and the index in the equivalent position in @indices then place the letter at that index in @result.

for @letters Z @indices -> ($letter, $index) {
    @result[$index] = $letter;
}

Then all we need to do is join @results up into a string and print it.

@result.join.say;

(Full code on Github.)

The Perl version is similar.

my @letters = split //, $string;
my @result;

In the absence of Z we just loop through the indexes of @indices and use that to find the equivalent position in @letters. So this won't work if @letters and @indices are of different length. That's not something we need to worry about for the examples though.

for my $i (0 .. scalar @indices - 1) {
    $result[$indices[$i]] = $letters[$i];
}

say join q{}, @result;

(Full code on Github.)

Challenge 2:

Zero Array

You are given an array of non-negative integers, @ints.

Write a script to return the minimum number of operations to make every element equal zero.

In each operation, you are required to pick a positive number less than or equal to the smallest element in the array, then subtract that from each positive element in the array.

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

operation 1: pick 1 => (0, 4, 0, 2, 4)
operation 2: pick 2 => (0, 2, 0, 0, 2)
operation 3: pick 2 => (0, 0, 0, 0, 0)
Example 2
Input: @ints = (0)
Output: 0
Example 3
Input: @ints = (2, 1, 4, 0, 3)
Output: 4

operation 1: pick 1 => (1, 0, 3, 0, 2)
operation 2: pick 1 => (0, 0, 2, 0, 1)
operation 3: pick 1 => (0, 0, 1, 0, 0)
operation 4: pick 1 => (0, 0, 0, 0, 0)

We start the Raku solution by assigning storage for a count of operations that might be performed.

my $ops = 0;

Then until all elements of @ints equal 0...

until @ints.all == 0 {

...we find the smallest element of @ints with .min()after filtering out all the 0's with .grep() so we don't get 0 every time.

    my $pick = @ints.grep({ $_ != 0 }).min;

Then we transform @ints with .map() so that each non-zero element is reduced by the value of $pick.

    @ints = @ints.map({ $_ == 0 ?? 0 !! $_ - $pick });

This is one operation as the spec calls it so we increment $ops.

    $ops++;
}

And finally we print out how many operations were performed.

say $ops;

(Full code on Github.)

The spec wasn't clear as to what should happen if one of the elements of @ints becomes negative as a result of an operation. This doesn't occur in any of the examples so I didn't think about it any further.

This is the Perl version.

my $ops = 0;

while (scalar grep {$_ != 0} @ints) {

I got around Perls' lack of .min() by sorting the non-zero elements of @ints and taking the first element which will be the smallest.

    my $pick = [sort { $a <=> $b } grep { $_ != 0 } @ints]->[0];
    @ints = map { $_ == 0 ? 0 : $_ - $pick } @ints;
    $ops++;
}

say $ops;

(Full code on Github.)