Perl Weekly Challenge 263.

My solutions (task 1 and task 2 ) to the The Weekly Challenge - 263.

Task 1: Target Index

Submitted by: Mohammad Sajid Anwar
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 $k[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

The task seems straightforward: sort the entries and filter the indices. I assume the arguments are in @ARGV starting with k.

Example 1:

perl -E '
$k=shift; @x=sort{$a<=>$b}@ARGV; say "k=$k, ints=(@ARGV) -> ", join " ", "(", grep({$x[$_]==$k} (0..@x-1)), ")";
' 2 1 5 3 2 4 2

Results:

k=2, ints=(1 5 3 2 4 2) -> ( 1 2 )

Example 2:

perl -E '
$k=shift; @x=sort{$a<=>$b}@ARGV; say "k=$k, ints=(@ARGV) -> ", join " ", "(", grep({$x[$_]==$k} (0..@x-1)), ")";
' 6 1 2 4 3 5

Results:

k=6, ints=(1 2 4 3 5) -> ( )

Example 3:

perl -E '
$k=shift; @x=sort{$a<=>$b}@ARGV; say "k=$k, ints=(@ARGV) -> ", join " ", "(", grep({$x[$_]==$k} (0..@x-1)), ")";
' 4 5 3 2 4 2 1

Results:

k=4, ints=(5 3 2 4 2 1) -> ( 4 )

The full code follows:

 1  # Perl weekly challenge 263
 2  # Task 1:  Target Index
 3  #
 4  # See https://wlmb.github.io/2024/04/01/PWC263/#task-1-target-index
 5  use v5.36;
 6  die <<~"FIN" unless @ARGV;
 7      Usage: $0 k N0 [N1...]
 8      to find the indices of the array N0 N1... (after sorting) for which
 9      N_i=k
10      FIN
11  my $k=shift;
12  my @sorted=sort{$a<=>$b}@ARGV;
13  say "k=$k, ints=(@ARGV) -> ",
14      join " ", "(", grep({$sorted[$_]==$k} (0..@sorted-1)), ")";

Example:

for i in "2 1 5 3 2 4 2" "6 1 2 4 3 5" "4 5 3 2 4 2 1";do ./ch-1.pl `echo $i`; done

Results:

k=2, ints=(1 5 3 2 4 2) -> ( 1 2 )
k=6, ints=(1 2 4 3 5) -> ( )
k=4, ints=(5 3 2 4 2 1) -> ( 4 )

Task 2: Merge Items

Submitted by: Mohammad Sajid Anwar
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] ]

As items to be merged may be part of the same list, it seems unnecesary to keep them in two lists. I use PDL to easily convert between several nested arrays to a simple list which I iterate using the new for_list. I accumulate values in a hash and finally, I print key,value pairs. This fits a longish one-liner.

Example 1:

perl -Mexperimental=for_list -MPDL -E '
push @e, pdl($_)->list for @ARGV;for my($x,$y)(@e){$v{$x}+=$y};@r=map {"[$_,$v{$_}]"} keys %v;say "@ARGV -> [@r]";
' "[[1,2],[2,3],[1,3],[3,2]]" "[[3,1],[1,3]]"

Results:

[[1,2],[2,3],[1,3],[3,2]] [[3,1],[1,3]] -> [[1,8] [3,3] [2,3]]

I got the expected results but unsorted.

Example 2:

perl -Mexperimental=for_list -MPDL -E '
push @e, pdl($_)->list for @ARGV;for my($x,$y)(@e){$v{$x}+=$y};@r=map {"[$_,$v{$_}]"} keys %v;say "@ARGV -> [@r]";
' "[[1,2],[2,3],[1,3],[3,2]]" "[[3,1],[1,3]]"

Results:

[[1,2],[2,3],[1,3],[3,2]] [[3,1],[1,3]] -> [[2,3] [3,3] [1,8]]

Example 3:

perl -Mexperimental=for_list -MPDL -E '
push @e, pdl($_)->list for @ARGV;for my($x,$y)(@e){$v{$x}+=$y};@r=map {"[$_,$v{$_}]"} keys %v;say "@ARGV -> [@r]";
' "[[1,1],[2,2],[3,3]]" "[[2,3],[2,4]]"

Results:

[[1,1],[2,2],[3,3]] [[2,3],[2,4]] -> [[1,1] [2,9] [3,3]]

The full code is similar. I just add some checks and I sort the result.

 1  # Perl weekly challenge 263
 2  # Task 2:  Merge Items
 3  #
 4  # See https://wlmb.github.io/2024/04/01/PWC263/#task-2-merge-items
 5  use v5.36;
 6  use PDL; # use the perl data language to convert the input to a list of keys-values
 7  use experimental qw(for_list); # use for list to process key-value pairs
 8  die <<~"FIN" unless @ARGV;
 9      Usage: $0 "[[K11,V11],[K12,V12]...]]" "[[K21,V21],[K22,V22],...]" ...
10      to accumulate all values Vij corresponding to each unique key Kn=Kij
11      FIN
12  my @keyvals;
13  push @keyvals, pdl($_)->list for @ARGV;
14  my %merged;
15  for my($key, $val)(@keyvals){
16      $merged{$key}+=$val
17  };
18  my @result = map {"[$_,$merged{$_}]"} sort {$a cmp $b} keys %merged;
19  say "@ARGV -> [@result]";

Example:

./ch-2.pl "[[1,2],[2,3],[1,3],[3,2]]" "[[3,1],[1,3]]"
./ch-2.pl "[[1,2],[2,3],[1,3],[3,2]]" "[[3,1],[1,3]]"
./ch-2.pl "[[1,1],[2,2],[3,3]]" "[[2,3],[2,4]]"

Results:

[[1,2],[2,3],[1,3],[3,2]] [[3,1],[1,3]] -> [[1,8] [2,3] [3,3]]
[[1,2],[2,3],[1,3],[3,2]] [[3,1],[1,3]] -> [[1,8] [2,3] [3,3]]
[[1,1],[2,2],[3,3]] [[2,3],[2,4]] -> [[1,1] [2,9] [3,3]]

Note that I may more or less than two input arrays.

Other examples:

./ch-2.pl "[[1,2],[2,3],[1,3],[3,2]]"
./ch-2.pl "[[1,2],[2,3],[1,3],[3,2]]" "[[3,1],[1,3]]" "[[1,1],[2,2],[3,3]]" "[[2,3],[2,4]]"

Results:

[[1,2],[2,3],[1,3],[3,2]] -> [[1,5] [2,3] [3,2]]
[[1,2],[2,3],[1,3],[3,2]] [[3,1],[1,3]] [[1,1],[2,2],[3,3]] [[2,3],[2,4]] -> [[1,9] [2,12] [3,6]]
Written on April 1, 2024