Camel
Peter
Peter Campbell Smith

More strings and arrays

Weekly challenge 322 — 19 May 2025

Week 322: 19 May 2025

Task 2

Task — Rank array

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.

Examples


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)

Analysis

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.

Try it 

Try running the script with any input:



example: 3, 1, 4, 1, 5, 9

Script


#!/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[)];
}


Output


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