## Perl Weekly Challenge 182: Max Index and Common Path

These are some answers to the Week 182 of the Perl Weekly Challenge organized by Mohammad S. Anwar.

Spoiler Alert: This weekly challenge deadline is due in a few of days from now (on Sept. 18, 2022 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.

You are given a list of integers.

Write a script to find the index of the first biggest number in the list.

Example:

``````Input: @n = (5, 2, 9, 1, 7, 6)
Output: 2 (as 3rd element in the list is the biggest number)

Input: @n = (4, 2, 3, 1, 5, 0)
Output: 4 (as 5th element in the list is the biggest number)
``````

### Max Index in Raku

The initial idea here was to to store the input data into a hash and to use the max routine to find the maximum value. The `max` documentation says:

Coerces the invocant to Iterable and returns the numerically largest element; in the case of Hashes, the Pair with the highest value.

But that did not work properly in my tests: `max` consistently appeared to return the pair with the highest key, not the highest value. I tried to use the maxpairs routine, which, according to the documentation,

returns a Seq with all of the Pairs with maximum value.

The `maxpairs` method works as expected. This leads to a very short program:

``````for (5, 2, 9, 1, 7, 6), (4, 2, 3, 1, 5, 0) -> @test {
my %nums = @test.kv;
say "@test[] : ", %nums.maxpairs;
}
``````

This program displays the following output:

``````\$ raku ./max-index.raku
5 2 9 1 7 6 : (2 => 9)
4 2 3 1 5 0 : (4 => 5)
``````

### Max Index in Perl

In Perl, we use a standard `for` loop to traverse the input list and find the index of the largest value:

``````use strict;
use warnings;
use feature qw/say/;

for my \$test ([5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0]) {
my @nums = @{\$test};
my (\$max_i, \$max_n) = (0, 0);
for my \$i (0..\$#nums) {
if (\$nums[\$i] > \$max_n) {
\$max_n = \$nums[\$i];
\$max_i = \$i;
}
}
say "@nums : \$max_i => \$max_n";
}
``````

This program displays the following output:

``````\$ perl ./max-index.pl
5 2 9 1 7 6 : 2 => 9
4 2 3 1 5 0 : 4 => 5
``````

### Max Index in 7 Other Languages

We have now implementations of the max index task in 7 languages as follows:

• Awk
• JavaScript
• Julia
• Python
• Ring
• Ruby
• Scala

#### Max Index in Awk

In awk, the data is normally passed as standard input. So we use a Unix pipe to pass the data to the program.

``````# run for example as:
# echo '5 2 9 1 7 6
# 4 2 3 1 5 0' | awk -f ./max-index.awk

function find_max() {
max_i = 0
max_n = \$1
for (i = 2; i < NF; i++) {
if (\$i > max_n) {
max_i = i
max_n = \$i
}
}
printf("Max index for %s: %d => %d\n", \$0, max_i - 1, max_n)
}
{
find_max()
}
``````

Output:

``````\$ echo '5 2 9 1 7 6
4 2 3 1 5 0' | awk -f ./max-index.awk
Max index for 5 2 9 1 7 6: 2 => 9
Max index for 4 2 3 1 5 0: 4 => 5
``````

#### Max Index in JavaScript

``````let tests = [ [5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0] ]
for  (let j = 0; j < tests.length; j++) {
let test = tests[j]
let max_i = 0
let max_n = test
for (let i = 1; i <= test.length; i++) {
if (test[i] > max_n) {
max_n = test[i]
max_i = i
}
}
console.log("Max index for " + test + ": " + max_i + " => " + max_n)
}
``````

Output:

``````Max index for 5,2,9,1,7,6: 2 => 9
Max index for 4,2,3,1,5,0: 4 => 5
``````

#### Max Index in Julia

Note that Julia indexes start at 1. So we need to subtract 1 from the `max_i` variable to get a result consistent with other (0-based index) languages.

``````for test in [[5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0]]
max_i = 1
max_n = test
for i in 2:length(test)
if (test[i] > max_n)
max_n = test[i]
max_i = i
end
end
println("Max index for \$test: \$(max_i - 1) => \$max_n)")
end
``````

Output:

``````\$ julia ./max-index.jl
Max index for [5, 2, 9, 1, 7, 6]: 2 => 9)
Max index for [4, 2, 3, 1, 5, 0]: 4 => 5)
``````

#### Max Index in Python

``````for test in [5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0]:
max_i = 0
max_n = test
for i in range(1, len(test)):
if test[i] > max_n:
max_n = test[i]
max_i = i
print("Max index for ", test, ": ", max_i, " => ", max_n)
``````

Output:

``````\$ python3 ./max-index.py
Max index for  [5, 2, 9, 1, 7, 6] :  2  =>  9
Max index for  [4, 2, 3, 1, 5, 0] :  4  =>  5
``````

#### Max Index in Ring

Note that Ring indexes start at 1 (like Julia indexes). So we need to subtract 1 from the max_i variable to get a result consistent with other (0-based index) languages.

``````for test in [[5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0]]
max_i = 1
max_n = test
str = ""
for i = 1 to len(test)
str = str + test[i] + " "
if test[i] > max_n
max_n = test[i]
max_i = i
ok
next
see "Max index for " + str + ": " +
(max_i - 1) + " => " + max_n + nl
next
``````

Output:

``````\$ ring ./max-index.ring
Max index for 5 2 9 1 7 6 : 2 => 9
Max index for 4 2 3 1 5 0 : 4 => 5
``````

#### Max Index in Ruby

``````for test in [[5, 2, 9, 1, 7, 6], [4, 2, 3, 1, 5, 0]]
max_i = 0
max_n = test
for i in 1..(test.length - 1)
if test[i] > max_n
max_n = test[i]
max_i = i
end
end
printf("Max index for %s: %d => %d\n", test.to_s, max_i, max_n)
end
``````

Output:

``````Max index for [5, 2, 9, 1, 7, 6]: 2 => 9
Max index for [4, 2, 3, 1, 5, 0]: 4 => 5
``````

#### Max Index in Scala

``````object fraction_tree extends App {

val tests: List[List[Int]] =
List(List(5, 2, 9, 1, 7, 6), List(4, 2, 3, 1, 5, 0))
for (test <- tests) {
var max_i = 0
var max_n = test(max_i)
for (i <- 1 to test.length - 1) {
if (test(i) > max_n) {
max_n = test(i)
max_i = i
}
}
println("Max index for " + test.mkString(" ") + s": \$max_i => \$max_n")
}
}
``````

Output:

``````Max index for 5 2 9 1 7 6: 2 => 9
Max index for 4 2 3 1 5 0: 4 => 5
``````

Given a list of absolute Linux file paths, determine the deepest path to the directory that contains all of them.

Example:

``````Input:
/a/b/c/1/x.pl
/a/b/c/d/e/2/x.pl
/a/b/c/d/3/x.pl
/a/b/c/4/x.pl
/a/b/c/d/5/x.pl

Ouput:
/a/b/c
``````

### Common Path in Raku

This program converts the input into an array of arrays and then compares each item of the first line with the corresponding item of the other lines. The program stops as soon as a difference is found.

``````my @input = qw <
/a/b/c/1/x.pl
/a/b/c/d/e/2/x.pl
/a/b/c/d/3/x.pl
/a/b/c/4/x.pl
/a/b/c/d/5/x.pl
>;

my @paths = gather {
for @input <-> \$line {
\$line ~~ s/^'/'//;
my @subpaths = split /'/'/, \$line;
take @subpaths[0..*-2];
}
}
my \$end = @paths.end;
my \$k = 0;
OUTLOOP: for 0..(@paths.end) -> \$i {
for 0..\$end -> \$j {
if @paths[\$j][\$i]:!exists or @paths[\$j][\$i] ne @paths[\$i] {
\$k = \$i - 1;
last OUTLOOP;
}
}
}
say '/', join '/', @paths[0..\$k];
``````

This program displays the following output:

``````\$ raku ./common-path.raku
/a/b/c
``````

### Common Path in Perl

``````use strict;
use warnings;
use feature qw/say/;

my @input = qw <
/a/b/c/1/x.pl
/a/b/c/d/e/2/x.pl
/a/b/c/d/3/x.pl
/a/b/c/4/x.pl
/a/b/c/d/5/x.pl
>;

my @paths;
for my \$line (@input) {
\$line =~ s|^/||;
my @subpaths = split /\//, \$line;
push @paths, [@subpaths[0..\$#subpaths-1]];
}

my @first = @{\$paths};
my \$end = \$#paths;
my \$k = 0;
OUTLOOP: for my \$i (0..\$#first) {
for my \$j (0..\$end) {
if ((not exists \$paths[\$j][\$i]) or \$paths[\$j][\$i] ne \$first[\$i]) {
\$k = \$i - 1;
last OUTLOOP;
}
}
}
say '/', join '/', @first[0..\$k];
``````

This program displays the following output:

``````\$ perl ./common-path.pl
/a/b/c
``````

## 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 September 25, 2022. And, please, also spread the word about the Perl Weekly Challenge if you can. I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.