Perl Weekly Challenge: Week 263

Challenge 1:

Target Index

You are given an array of integers, @ints and an integer $k.

Write a script to return the list of indices in the sorted array where the element is same as the given target element.

Example 1
Input: @ints = (1, 5, 3, 2, 4, 2), $k = 2
Output: (1, 2)

Sorted array: (1, 2, 2, 3, 4, 5)
Target indices: (1, 2) as $ints[1] = 2 and $ints[2] = 2
Example 2
Input: @ints = (1, 2, 4, 3, 5), $k = 6
Output: ()

No element in the given array matching the given target.
Example 3
Input: @ints = (5, 3, 2, 4, 2, 1), $k = 4
Output: (4)

Sorted array: (1, 2, 2, 3, 4, 5)
Target index: (4) as $ints[4] = 4

Not a one-liner this week but it's close.

First we create a sorted version of @ints which is dead simple with .sort().

my @sorted = @ints.sort;

Then we get all the indices of the elements of @sorted and go through the array with .grep() finding the indices whose corresponding elements equal $k. The rest of the code is just for pretty-printing these indices in the same format as the examples.

say q{(}, @sorted.keys.grep({ @sorted[$_] == $k}).join(q{, }), q{)};

(Full code on Github.)

The Perl version is almost the same except the order of operations is right to left. Having chained code operations read from left to right is just of the many simple but nice quality of life improvements that Raku provides over Perl. (Though Arabic or Hebrew speakers might not agree.)

my  @sorted = sort { $a <=> $b } @ints;
say q{(}, (join q{, }, grep { $sorted[$_] == $k} keys @sorted), q{)};

(Full code on Github.)

Challenge 2:

Merge Items

You are given two 2-D array of positive integers, $items1 and $items2 where element is pair of (item_id, item_quantity).

Write a script to return the merged items.

Example 1
Input: $items1 = [ [1,1], [2,1], [3,2] ]
       $items2 = [ [2,2], [1,3] ]
Output: [ [1,4], [2,3], [3,2] ]

Item id (1) appears 2 times: [1,1] and [1,3]. Merged item now (1,4)
Item id (2) appears 2 times: [2,1] and [2,2]. Merged item now (2,3)
Item id (3) appears 1 time: [3,2]
Example 2
Input: $items1 = [ [1,2], [2,3], [1,3], [3,2] ]
       $items2 = [ [3,1], [1,3] ]
Output: [ [1,8], [2,3], [3,3] ]
Example 3
Input: $items1 = [ [1,1], [2,2], [3,3] ]
       $items2 = [ [2,3], [2,4] ]
Output: [ [1,1], [2,9], [3,3] ]

First a hash is created to store the merged items. Its' keys will be the items ids, and the values will be the items quantity.

my %merged;

Getting the command-line input into the script is the hardest part of the solution really. I chose to have the input (for e.g. example 1) look like this: "1,1 2,1 3,2" "2,2 1,3". $items1 and $items2 can be concatenated as they will be treated exactly the same in the merge process. Once we have .split() and .map()ed the input into individual items...

for "$items1 $items2".split(/\s+/).map({ $_.split(/\,/) }) -> $item {

...item is added to %merged.

    %merged{@$item[0]} += @$item[1];
}

Once all items are processed, we should have a hash whose keys are unique item ids and whose values are cumulative total item quantities.

This next bit looks complicated but it is mainly because I was trying to format the output in the style of the examples.

say q{[ }, 
    %merged

The only important part is this line which .sort()s the %merged hash in numeric ascending order.

        .sort
        .map({ "[{$_.key},{$_.value}]" })
        .join(q{, }),
q{ ]};

(Full code on Github.)

And this is the Perl version.

my %merged;
foreach my $item (map { [split /\,/] } split /\s+/, "$items1 $items2") {
    $merged{$item->[0]} += $item->[1];
}

say q{[ }, 
    (join q{, }, map { "[$_,$merged{$_}]" } sort { $merged{$b} <=> $merged{$a} } keys %merged),
q{ ]};

(Full code on Github.)