Peter’s blog ✴ Week 322 ✴ 19 May 2025
THE WEEKLY CHALLENGE
More strings and arrays
You are given an array of integers. Write a script to return an array of the ranks of each element: the lowest value has rank 1, next lowest rank 2, etc. If two elements are the same then they share the same rank.
Example 1 Input: @ints = (55, 22, 44, 33) Output: (4, 1, 3, 2) Example 2 Input: @ints = (10, 10, 10) Output: (1, 1, 1) Example 3 Input: @ints = (5, 1, 1, 4, 3) Output: (4, 1, 1, 3, 2)
My algorithm is as follows:
Create a hash %numbers with the numbers from the input array as the keys. This automatically eliminates duplicates.
Create an array @rank like this:
$rank[$_] = $j ++ for sort {$a <=> $b} keys %numbers;
which creates an array @rank
such that the rank of value $array[$i] is $rank[$i] - for
example, the rank of the lowest unique value in the input array is
1, the next lowest 2 and so on.
Create the desired output array, @ranking, by setting:
$ranking[$_] = $rank[$array[$_]] for 0 .. $#array;
which creates the desired output array, @ranking
where $ranking[$i] contains the rank of element $i
in the input array.
This works for any input array values that are positive, but the
challenge states 'integers' rather than 'positive integers'.
To cope with that, I first determine the minimum value $min in @array
and then offset the indices of @rank by $min so that they
are never negative.
Each solution is accompanied by concise reasoning, making it easy to follow the logic and adapt the code for similar problems.
This review may cover either or both challenges for this week.
#!/usr/bin/perl # Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge use v5.26; # The Weekly Challenge - 2025-05-19 use utf8; # Week 322 - task 2 - Rank array use warnings; # Peter Campbell Smith binmode STDOUT, ':utf8'; use Encode; use List::Util 'min'; rank_array(55, 22, 44, 33); rank_array(10, 10, 10); rank_array(5, 1, 1, 4, 3); rank_array(0); rank_array(-3, -3, -3, -2, -2, -1, 0); # bigger example my @array; push @array, int(rand(100)) for 0 .. 25; rank_array(@array); sub rank_array { my (@array, @rank, @ranking, $n, $j, %numbers, $min); # initialise @array = @_; # to cope with -ve numbers $min = min(@array); # make # with keys of unique numbers $numbers{$_} = 1 for @array; # rank the numbers smallest to largest $j = 1; $rank[$_ - $min] = $j ++ for sort {$a <=> $b} keys %numbers; # assign the ranks to @ranking in the original order $ranking[$_] = $rank[$array[$_] - $min] for 0 .. $#array; say qq[\nInput: (] . join(', ', @array) . q[)]; say qq[Output: (] . join(', ', @ranking) . q[)]; }
10 lines of code
Input: (55, 22, 44, 33) Output: (4, 1, 3, 2) Input: (10, 10, 10) Output: (1, 1, 1) Input: (5, 1, 1, 4, 3) Output: (4, 1, 1, 3, 2) Input: (0) Output: (1) Input: (-3, -3, -3, -2, -2, -1, 0) Output: (1, 1, 1, 2, 2, 3, 4) Input: (8, 57, 77, 28, 10, 65, 28, 3, 67, 68, 24, 84, 52, 80, 64, 66, 80, 81, 95, 53, 47, 83, 57, 81, 86, 11) Output: (2, 10, 16, 6, 3, 12, 6, 1, 14, 15, 5, 20, 8, 17, 11, 13, 17, 18, 22, 9, 7, 19, 10, 18, 21, 4)
Any content of this website which has been created by Peter Campbell Smith is in the public domain