Peter’s blog ✴ Week 214 ✴ 24 April 2023

THE WEEKLY CHALLENGE
All about points

The Perl Camel

Task 1

Rank score

You are given a list of scores (>=1). Write a script to rank each score in descending order. First three will get medals i.e. G (Gold), S (Silver) and B (Bronze). Rest will just get the ranking number. Use the standard model of giving equal scores equal rank, then advancing that number of ranks.

Examples


Example 1
Input:  = (1,2,4,3,5)
Output: (5,4,S,B,G)
Score 1 is the 5th rank.
Score 2 is the 4th rank.
Score 4 is the 2nd rank i.e. Silver (S).
Score 3 is the 3rd rank i.e. Bronze (B).
Score 5 is the 1st rank i.e. Gold (G).

Example 2
Input:  = (8,5,6,7,4)
Output: (G,4,B,S,5)
Score 8 is the 1st rank i.e. Gold (G).
Score 4 is the 4th rank.
Score 6 is the 3rd rank i.e. Bronze (B).
Score 7 is the 2nd rank i.e. Silver (S).
Score 4 is the 5th rank.

Example 3
Input:  = (3,5,4,2)
Output: (B,G,S,4)

Example 4
Input:  = (2,5,2,1,7,5,1)
Output: (4,S,4,6,G,S,6)

Analysis

This is perhaps not as easy as it sounds, but I did it like this:

  • Make an array of @rank_symbols: (G, S, B, 4, 5 ...) of size equal to the number of players.
  • Sort the list of @scores in ascending order (@sorted)
  • Loop over the sorted list setting $ranks[$sorted[$s]] = $rank_symbols[$s]

So we now have the original list in $scores[$s], and the ranks (G, S, B ...) in the same order in $ranks[$sorted[$s]].

The only slight wrinkle is when there is a tie, which is fixed with:

$ranks[$sorted[$s]] .= '=' 
    if ($s > 0 and $sorted[$s] == $sorted[$s - 1]);

Try it 

Example: 8, 5, 6, 7, 4, 4

Script


#!/usr/bin/perl

use v5.16;    # The Weekly Challenge - 2023-04-24
use utf8;     # Week 214 task 1 - Rank score
use strict;   # Peter Campbell Smith
use warnings; # Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

rank_score(8, 5, 7, 6, 4);
rank_score(8, 5, 7, 6, 5, 4);
rank_score(1, 2, 2, 2, 3);
rank_score(1, 12, 123, 1234, 1234, 2);

sub rank_score {
    
    my (@scores, $num_players, @rank_symbols, @ranks, $s, $score, $rank, $prev, @sorted, $p,
        $rubric1, $rubric2, $w);
    
    # process input
    @scores = @_;
    $num_players = scalar @scores - 1;
    
    # create rank symbols - GBS456 ...
    $rank_symbols[$num_players - $_] = $_ > 2 ? $_ + 1: substr('GSB', $_, 1) for (0 .. $num_players);

    # loop over players sorted by score
    @sorted = sort {$a <=> $b} @scores;
    $w = 0;
    for ($s = 0; $s <= $num_players; $s ++) {
        
        # assign rank symbol to score
        $ranks[$sorted[$s]] = $rank_symbols[$s];
        
        # deal with tied place
        $ranks[$sorted[$s]] .= '=' if ($s > 0 and $sorted[$s] == $sorted[$s - 1]);
        
        # find largest width for neat printout
        $w = length($sorted[$s]) if $w < length($sorted[$s]);
        $w = length($ranks[$sorted[$s]]) if $w < length($ranks[$sorted[$s]]);
    }
    
    # show answers
    for ($p = 0; $p <= $num_players; $p ++) {
        $rubric1 .= sprintf("%${w}s, ", $scores[$p]);
        $rubric2 .= sprintf("%${w}s, ", $ranks[$scores[$p]]);
    }
    say qq[\nInput:  ] . substr($rubric1, 0, -2);
    say qq[Output: ] . substr($rubric2, 0, -2);
}

18 lines of code

Output from script


Input:  8, 5, 7, 6, 4
Output: G, 4, S, B, 5

Input:   8,  5,  7,  6,  5,  4
Output:  G, 4=,  S,  B, 4=,  6

Input:   1,  2,  2,  2,  3
Output:  5, S=, S=, S=,  G

Input:     1,   12,  123, 1234, 1234,    2
Output:    6,    4,    B,   G=,   G=,    5

 

Any content of this website which has been created by Peter Campbell Smith is in the public domain