Target Merge
with Raku

by Arne Sommer

Target Merge with Raku

[283] Published 4. April 2024.

This is my response to The Weekly Challenge #263.

Challenge #263.1: Target Index

You are given an array of integers, @ints and a target element $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
File: target-index
#! /usr/bin/env raku

unit sub MAIN (Int $k,                                             # [1]
               *@ints where @ints.elems > 0 && all(@ints) ~~ Int,  # [2]
               :v(:$verbose));

say ": Sorted: { @ints.sort.join(",") }" if $verbose;

say '(', (@ints.sort.grep: $k, :k).join(", "), ')';                # [3]

[1] First the $k value, which must be an integer.

[2] Then the array of integers, at least one element.

[3] sort the array, then use grep to filter out the $k values - or rather the indices instead of the values themselves as indicated with the :k adverb. Pretty print the result.

See docs.raku.org/routine/grep for more information about grep.

See Unique Rank with Raku for a short description of the :k (keys) adverb.

Running it:

$ ./target-index 2 1 5 3 2 4 2
(1, 2)

$ ./target-index 6 1 2 4 3 5
()

$ ./target-index 4 5 3 2 4 2 1
(4)

With verbose mode:

: Sorted: 1,2,2,3,4,5
(1, 2)

$ ./target-index -v 6 1 2 4 3 5
: Sorted: 1,2,3,4,5
()

$ ./target-index -v 4 5 3 2 4 2 1
: Sorted: 1,2,2,3,4,5
(4)

Challenge #263.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] ]
File: merge-items
#! /usr/bin/env raku

unit sub MAIN ($items1, $items2, :v(:$verbose));     # [1]

my @items1 = $items1.split("|")>>.words>>.Int;       # [2]
my @items2 = $items2.split("|")>>.words>>.Int;       # [2]

if $verbose
{
  say ": items1: { @items1.raku }";
  say ": items2: { @items2.raku }";
}

my %count;                                           # [3]

for @items1 -> $item                                 # [4]
{
  say ": item1 ID $item[0] with frequency $item[1]";
  %count{$item[0]} += $item[1];                      # [4a]
}

for @items2 -> $item                                 # [5]
{
  say ": item2 ID $item[0] with frequency $item[1]";
  %count{$item[0]} += $item[1];                      # [5a]
}

my @output;                                          # [6]

for sort keys %count -> $key                         # [7]
{
  @output.push: ($key.Int, %count{$key});            # [7a]
}

say @output.raku;                                    # [8]

[1] The two two dimentional arrays. See the "Running it" section below for how to specify the values.

[2] Convert the strings into two dimentional arrays.

[3] We are going to count the frequencies, using this hash.

[4] Iterate over the first array; add the count (the second part of the pair) to the value (the first part of the pair) in the hash [4a].

[5] As above, for the second array.

[6] The two dimentional array for the output output will end up here.

[7] Iterate over the keys (the original values) in sorted order. Add the key and the value (frequency) as a pair to the result. Note the coersion to integer, as it is a string otherwise.

[8] Print the result, uisng the raku method.

See docs.raku.org/routine/raku for more information about raku.

Running it:

$ ./merge-items "1 1 | 2 1 | 3 2" "2 2 | 1 3"
[(1, 4), (2, 3), (3, 2)]

$ ./merge-items "1 2 | 2 3 | 1 3 | 3 2" "3 1 | 1 3"
[(1, 8), (2, 3), (3, 3)]

$ ./merge-items "1 1 | 2 2 | 3 3" "2 3 | 2 4"
[(1, 1), (2, 9), (3, 3)]

Looking good.

With verbose mode:

$ ./merge-items -v "1 1 | 2 1 | 3 2" "2 2 | 1 3"
: items1: [(1, 1), (2, 1), (3, 2)]
: items2: [(2, 2), (1, 3)]
: item1 ID 1 with frequency 1
: item1 ID 2 with frequency 1
: item1 ID 3 with frequency 2
: item2 ID 2 with frequency 2
: item2 ID 1 with frequency 3
[(1, 4), (2, 3), (3, 2)]

$ ./merge-items -v "1 2 | 2 3 | 1 3 | 3 2" "3 1 | 1 3"
: items1: [(1, 2), (2, 3), (1, 3), (3, 2)]
: items2: [(3, 1), (1, 3)]
: item1 ID 1 with frequency 2
: item1 ID 2 with frequency 3
: item1 ID 1 with frequency 3
: item1 ID 3 with frequency 2
: item2 ID 3 with frequency 1
: item2 ID 1 with frequency 3
[(1, 8), (2, 3), (3, 3)]

$ ./merge-items -v "1 1 | 2 2 | 3 3" "2 3 | 2 4"
: items1: [(1, 1), (2, 2), (3, 3)]
: items2: [(2, 3), (2, 4)]
: item1 ID 1 with frequency 1
: item1 ID 2 with frequency 2
: item1 ID 3 with frequency 3
: item2 ID 2 with frequency 3
: item2 ID 2 with frequency 4
[(1, 1), (2, 9), (3, 3)]

And that's it.