Perl Weekly Challenge 226: Shuffle String

These are some answers to the Week 226, Task 1, of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Spoiler Alert: This weekly challenge deadline is due in a few days from now (on July 23, 2023 at 23:59). This blog post offers some solutions to this challenge. Please don’t read on if you intend to complete the challenge on your own.

Task 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'

I first thought that the indice represented the order in which to pick the letters from the string, and quickly came up with the following simple Raku subroutine:

sub shuffle-string ($string, @indices) {
    # Caution: wrong solution
    return ($string.comb)[@indices].join("");
}

and ran it with the first example provided with task:

$string = 'lacelengh', @indices = (3,2,0,5,4,8,6,7,1)

But that doesn't work, as, instead of "challenge", it returns the following string:

eclelhnga

What the heck is this? After some checks, I quickly came to the conclusion that my program did not appear to be buggy, but that I had misunderstood the task. How the hell can we obtain the string "challenge" from the given input data? It took me a few minutes to understand. Looking at the letters of the input string and the input indice:

[l a c e l e n g h]
(3 2 0 5 4 8 6 7 1)

we can see that it works differently: we need to pick the letter whose position is that of 0 in the indice (i.e. 'c'), then the letter whose position is that of 1 in the indice, and so on:

0   c
1   h
2   a
3   l
4   l
5   e
6   n
7   g
8   e

So we need to transform the input @indice array, or reverse it if you prefer, if we want to use it as a "slice" on the array containing the letters of the input string. The idea is to construct a new array where the first integer will be the position corresponding to 0 in the original @indice array (i.e. letter 'c'), the second item will be the position corresponding to 1 in the original @indice array (letter 'h'), and so on so that we will end up with the following new array:

[2 8 1 0 4 3 6 7 5]

which can readily used as a slice over the arrays of letters of the original string.

Shuffle String in Raku

Please refer to the above explanations to understand the array transformation (@indices -> @index) performed by the program.

sub shuffle-string ($string, @indice) {
    my @index;
    @index[@indice[$_]]= $_ for 0..@indice.end;
    # say @index;
    return ($string.comb)[@index].join("");
}

for ('lacelengh', (3,2,0,5,4,8,6,7,1)),
    ('rulepark', (4,7,3,1,0,5,2,6)) -> @test {
    printf "%-10s - %-20s => ", @test[0], "@test[1]";
    say shuffle-string @test[0], @test[1];
}

This program displays the following output:

$ raku ./shuffle-string.raku
lacelengh  - 3 2 0 5 4 8 6 7 1    => challenge
rulepark   - 4 7 3 1 0 5 2 6      => perlraku

Shuffle String in Perl

This a port to Perl of the above Raku program. Please refer to the explanations after the task description if you need any further explanation.

use strict;
use warnings;
use feature 'say';

sub shuffle_string {
    my ($string, $idx_ref) = @_;
    my @indices = @$idx_ref;
    my @index;
    @index[$indices[$_]]= $_ for 0..$#indices;
    return join "", (split //, $string)[@index];
}

for my $test (['lacelengh', [3,2,0,5,4,8,6,7,1]],
    ['rulepark', [4,7,3,1,0,5,2,6]]) {
    printf "%-10s - %-18s => ", 
        $test->[0], "@{$test->[1]}";
    say shuffle_string $test->[0], $test->[1];

}

This program displays the following output:

$ perl ./shuffle-string.pl
lacelengh  - 3 2 0 5 4 8 6 7 1  => challenge
rulepark   - 4 7 3 1 0 5 2 6    => perlraku

Wrapping up

The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on July 30 , 2023. And, please, also spread the word about the Perl Weekly Challenge if you can.

Leave a comment

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.