Perl Weekly Challenge 289: Third Maximum

These are some answers to the Week 289, 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 October 6, 2024, at 23:59). This blog post provides some solutions to this challenge. Please don’t read on if you intend to complete the challenge on your own.

Task 1: Third Maximum

You are given an array of integers, @ints.

Write a script to find the third distinct maximum in the given array. If third maximum doesn’t exist then return the maximum number.

Example 1

Input: @ints = (5, 6, 4, 1)
Output: 4

The first distinct maximum is 6.
The second distinct maximum is 5.
The third distinct maximum is 4.

Example 2

Input: @ints = (4, 5)
Output: 5

In the given array, the third maximum doesn't exist therefore returns the maximum.

Example 3

Input: @ints =  (1, 2, 2, 3)
Output: 1

The first distinct maximum is 3.
The second distinct maximum is 2.
The third distinct maximum is 1.

There are several possible interpretations to the word "distinct" in the task specification. I understand that each value should be used only once to figure out the third maximum value. Therefore, the first step in our program will be to remove duplicates from the input list.

Third Maximum in Raku

Our program uses the unique, sort and reverse to remove duplicates and obtain the input list sorted in descending order. Then, we return the third item (subscript 2) if it exists, otherwise return the first element.

sub third-max (@in) {
    my @sorted = @in.unique.sort.reverse;   
    return @sorted[2]:exists ?? @sorted[2] !! @sorted[0];
}

my @tests = (5, 6, 4, 1), (4, 5), (1, 2, 2, 3);
for @tests -> @test {
    printf "%-10s => ", "@test[]";
    say third-max @test;
}

This program displays the following output:

$ raku ./third-maximum.raku
5 6 4 1    => 4
4 5        => 5
1 2 2 3    => 1

Third Maximum in Perl

This is a port to Perl of the above Raku program. In Perl, the easiest way to remove duplicates is to store the values in a hash (here, %unique) and then to retrieve the keys. Note that we have to specify a numeric sort (with the <=> operator) and, since we do that, we can put the parameters $a and $b in reverse order in order to obtain directly a descending order, without using a reverse.

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

sub third_max {
    my %unique = map { $_ => 1 } @_;    
    my @sorted = sort { $b <=> $a } keys %unique;
    return exists $sorted[2] ? $sorted[2] : $sorted[0];
}

my @tests = ( [5, 6, 4, 1], [4, 5], [1, 2, 2, 3] );
for my $test (@tests) {
    printf "%-10s => ", "@$test"; 
    say third_max @$test;
}

This program displays the following output:

$ perl third-maximum.pl
5 6 4 1    => 4
4 5        => 5
1 2 2 3    => 1

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 October 13, 2024. 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.