Ranking methods: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Racket}}: actual implementation added)
Line 1,038: Line 1,038:
7 7 4 7 7 39 Stephen
7 7 4 7 7 39 Stephen
</pre>
</pre>
=={{header|Scala}}==
This example uses a type-safe singly-linked object model with no mutable state variables, which makes it longer than the Ruby version but demonstrates object-oriented functional programming style.
<lang Scala>object RankingMethods extends App {
case class Score(score: Int, name: String) // incoming data
case class Rank[Precision](rank: Precision, names: List[String]) // outgoing results (can be int or double)
case class State[Precision](n: Int, done: List[Rank[Precision]]) { // internal state, no mutable variables
def next(n: Int, next: Rank[Precision]) = State(n, next :: done)
}
def grouped[Precision](list: List[Score]) = // group names together by score, with highest first
(scala.collection.immutable.TreeMap[Int, List[Score]]() ++ list.groupBy(-_.score))
.values.map(_.map(_.name)).foldLeft(State[Precision](1, Nil)) _

// Ranking methods:

def rankStandard(list: List[Score]) =
grouped[Int](list){case (state, names) => state.next(state.n+names.length, Rank(state.n, names))}.done.reverse

def rankModified(list: List[Score]) =
rankStandard(list).map(r => Rank(r.rank+r.names.length-1, r.names))

def rankDense(list: List[Score]) =
grouped[Int](list){case (state, names) => state.next(state.n+1, Rank(state.n, names))}.done.reverse

def rankOrdinal(list: List[Score]) =
list.zipWithIndex.map{case (score, n) => Rank(n+1, List(score.name))}

def rankFractional(list: List[Score]) =
rankStandard(list).map(r => Rank((2*r.rank+r.names.length-1.0)/2, r.names))

// Tests:

def parseScores(s: String) = s split "\\s+" match {case Array(s,n) => Score(s.toInt, n)}
val test = List("44 Solomon", "42 Jason", "42 Errol", "41 Garry", "41 Bernard", "41 Barry", "39 Stephen").map(parseScores)

println("Standard:")
println(rankStandard(test) mkString "\n")
println("\nModified:")
println(rankModified(test) mkString "\n")
println("\nDense:")
println(rankDense(test) mkString "\n")
println("\nOrdinal:")
println(rankOrdinal(test) mkString "\n")
println("\nFractional:")
println(rankFractional(test) mkString "\n")

}</lang>
{{out}}
<pre>Standard:
Rank(1,List(Solomon))
Rank(2,List(Jason, Errol))
Rank(4,List(Garry, Bernard, Barry))
Rank(7,List(Stephen))

Modified:
Rank(1,List(Solomon))
Rank(3,List(Jason, Errol))
Rank(6,List(Garry, Bernard, Barry))
Rank(7,List(Stephen))

Dense:
Rank(1,List(Solomon))
Rank(2,List(Jason, Errol))
Rank(3,List(Garry, Bernard, Barry))
Rank(4,List(Stephen))

Ordinal:
Rank(1,List(Solomon))
Rank(2,List(Jason))
Rank(3,List(Errol))
Rank(4,List(Garry))
Rank(5,List(Bernard))
Rank(6,List(Barry))
Rank(7,List(Stephen))

Fractional:
Rank(1.0,List(Solomon))
Rank(2.5,List(Jason, Errol))
Rank(5.0,List(Garry, Bernard, Barry))
Rank(7.0,List(Stephen))</pre>


=={{header|Tcl}}==
=={{header|Tcl}}==

Revision as of 13:08, 2 October 2014

Task
Ranking methods
You are encouraged to solve this task according to the task description, using any language you may know.

The numerical rank of competitors in a competition shows if one is better than, equal to, or worse than another based on their results in a competition.

The numerical rank of a competitor can be assigned in several different ways.

Task

The following scores are accrued for all competitors of a competition (in best-first order):

44 Solomon
42 Jason
42 Errol
41 Garry
41 Bernard
41 Barry
39 Stephen

For each of the following ranking methods, create a function/method/procedure/subroutine... that applies the ranking method to an ordered list of scores with scorers:

  1. Standard. (Ties share what would have been their first ordinal number).
  2. Modified. (Ties share what would have been their last ordinal number).
  3. Dense. (Ties share the next available integer).
  4. Ordinal. ((Competitors take the next available integer. Ties are not treated otherwise).
  5. Fractional. (Ties share the mean of what would have been their ordinal numbers).

See the wikipedia article for a fuller description.

Show here, on this page, the ranking of the test scores under each of the numbered ranking methods.

AWK

Translation of: Python

This uses separate files for each method of ranking: <lang awk>##

    1. Dense ranking in file: ranking_d.awk

BEGIN{ lastresult = "!"; lastrank = 0 }

function d_rank(){

   if($1==lastresult){
       print lastrank, $0
   }else{
       lastresult = $1
       print ++lastrank, $0 }

} //{d_rank() }

    1. Fractional ranking in file: ranking_f.awk

BEGIN{

   last = "!"
   flen = 0 }

function f_rank(){

   item = $0
   if($1!=last){
       if(flen){
           sum = 0
           for(fl=0; fl < flen;){
               $0 = fifo[fl++]
               sum += $1 }
           mean = sum / flen
           for(fl=0; fl < flen;){
               $0 = fifo[fl++]
               $1 = ""
               printf("%3g %s\n", mean, $0) }
           flen = 0
   }}
   $0 = item
   last = $1
   fifo[flen++] = sprintf("%i %s", FNR, item)

} //{f_rank()}

END{ if(flen){

       sum = 0
       for(fl=0; fl < flen;){
           $0 = fifo[fl++]
           sum += $1 }
       mean = sum / flen
       for(fl=0; fl < flen;){
           $0 = fifo[fl++]
           $1 = ""
           printf("%3g %s\n", mean, $0) }}}
    1. Modified competition ranking in file: ranking_mc.awk

BEGIN{

   lastresult = "!"
   flen = 0 }

function mc_rank(){

   if($1==lastresult){
       fifo[flen++] = $0
   }else{
       for(fl=0; fl < flen;){
           print FNR-1, fifo[fl++]}
       flen = 0
       fifo[flen++] = $0
       lastresult = $1}

} //{mc_rank()}

END{ for(fl=0; fl < flen;){

       print FNR, fifo[fl++]} }
    1. Ordinal ranking in file: ranking_o.awk

function o_rank(){ print FNR, $0 } //{o_rank() }

    1. Standard competition ranking in file: ranking_sc.awk

BEGIN{ lastresult = lastrank = "!" }

function sc_rank(){

   if($1==lastresult){
       print lastrank, $0
   }else{
       print FNR, $0
       lastresult = $1
       lastrank = FNR}

} //{sc_rank()} </lang>

The input as a file ranking.txt:

44 Solomon
42 Jason
42 Errol
41 Garry
41 Bernard
41 Barry
39 Stephen
Output:
C:\Users\RC\Code>awk -f ranking_sc.awk ranking.txt
1 44 Solomon
2 42 Jason
2 42 Errol
4 41 Garry
4 41 Bernard
4 41 Barry
7 39 Stephen

C:\Users\RC\Code>awk -f ranking_mc.awk ranking.txt
1 44 Solomon
3 42 Jason
3 42 Errol
6 41 Garry
6 41 Bernard
6 41 Barry
7 39 Stephen

C:\Users\RC\Code>awk -f ranking_d.awk ranking.txt
1 44 Solomon
2 42 Jason
2 42 Errol
3 41 Garry
3 41 Bernard
3 41 Barry
4 39 Stephen

C:\Users\RC\Code>awk -f ranking_o.awk ranking.txt
1 44 Solomon
2 42 Jason
3 42 Errol
4 41 Garry
5 41 Bernard
6 41 Barry
7 39 Stephen

C:\Users\RC\Code>awk -f ranking_f.awk ranking.txt
  1  44 Solomon
2.5  42 Jason
2.5  42 Errol
  5  41 Garry
  5  41 Bernard
  5  41 Barry
  7  39 Stephen

C:\Users\RC\Code>

Go

<lang go>package main

import ( "fmt" "sort" )

type rankable interface { Len() int RankEqual(int, int) bool }

func StandardRank(d rankable) []float64 { r := make([]float64, d.Len()) var k int for i := range r { if i == 0 || !d.RankEqual(i, i-1) { k = i + 1 } r[i] = float64(k) } return r }

func ModifiedRank(d rankable) []float64 { r := make([]float64, d.Len()) for i := range r { k := i + 1 for j := i + 1; j < len(r) && d.RankEqual(i, j); j++ { k = j + 1 } r[i] = float64(k) } return r }

func DenseRank(d rankable) []float64 { r := make([]float64, d.Len()) var k int for i := range r { if i == 0 || !d.RankEqual(i, i-1) { k++ } r[i] = float64(k) } return r }

func OrdinalRank(d rankable) []float64 { r := make([]float64, d.Len()) for i := range r { r[i] = float64(i + 1) } return r }

func FractionalRank(d rankable) []float64 { r := make([]float64, d.Len()) for i := 0; i < len(r); { var j int f := float64(i + 1) for j = i + 1; j < len(r) && d.RankEqual(i, j); j++ { f += float64(j + 1) } f /= float64(j - i) for ; i < j; i++ { r[i] = f } } return r }

type scores []struct { score int name string }

func (s scores) Len() int { return len(s) } func (s scores) RankEqual(i, j int) bool { return s[i].score == s[j].score } func (s scores) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s scores) Less(i, j int) bool { if s[i].score != s[j].score { return s[i].score > s[j].score } return s[i].name < s[j].name }

var data = scores{ {44, "Solomon"}, {42, "Jason"}, {42, "Errol"}, {41, "Garry"}, {41, "Bernard"}, {41, "Barry"}, {39, "Stephen"}, }

func main() { show := func(name string, fn func(rankable) []float64) { fmt.Println(name, "Ranking:") r := fn(data) for i, d := range data { fmt.Printf("%4v - %2d %s\n", r[i], d.score, d.name) } }

sort.Sort(data) show("Standard", StandardRank) show("\nModified", ModifiedRank) show("\nDense", DenseRank) show("\nOrdinal", OrdinalRank) show("\nFractional", FractionalRank) }</lang>

Output:
Standard Ranking:
   1 - 44 Solomon
   2 - 42 Errol
   2 - 42 Jason
   4 - 41 Barry
   4 - 41 Bernard
   4 - 41 Garry
   7 - 39 Stephen

Modified Ranking:
   1 - 44 Solomon
   3 - 42 Errol
   3 - 42 Jason
   6 - 41 Barry
   6 - 41 Bernard
   6 - 41 Garry
   7 - 39 Stephen

Dense Ranking:
   1 - 44 Solomon
   2 - 42 Errol
   2 - 42 Jason
   3 - 41 Barry
   3 - 41 Bernard
   3 - 41 Garry
   4 - 39 Stephen

Ordinal Ranking:
   1 - 44 Solomon
   2 - 42 Errol
   3 - 42 Jason
   4 - 41 Barry
   5 - 41 Bernard
   6 - 41 Garry
   7 - 39 Stephen

Fractional Ranking:
   1 - 44 Solomon
 2.5 - 42 Errol
 2.5 - 42 Jason
   5 - 41 Barry
   5 - 41 Bernard
   5 - 41 Garry
   7 - 39 Stephen

J

Implementation:

<lang J>competitors=:<;._1;._2]0 :0

44 Solomon
42 Jason
42 Errol
41 Garry
41 Bernard
41 Barry
39 Stephen

)

scores=: {."1

standard=: 1+i.~ modified=: 1+i:~ dense=: #/.~ # #\@~. ordinal=: #\ fractional=: #/.~ # ] (+/%#)/. #\

rank=:1 :'<"0@u@:scores,.]'</lang>

Note that we assume that the competitors are already in the right order. Also, of course (as is common when using J) we use the J command line, because that is portable across operating systems (for example: the OS command line is difficult to use on phones).

Task examples:

<lang J> standard rank competitors ┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │2│42│Jason │ ├─┼──┼───────┤ │2│42│Errol │ ├─┼──┼───────┤ │4│41│Garry │ ├─┼──┼───────┤ │4│41│Bernard│ ├─┼──┼───────┤ │4│41│Barry │ ├─┼──┼───────┤ │7│39│Stephen│ └─┴──┴───────┘

  modified rank competitors

┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │3│42│Jason │ ├─┼──┼───────┤ │3│42│Errol │ ├─┼──┼───────┤ │6│41│Garry │ ├─┼──┼───────┤ │6│41│Bernard│ ├─┼──┼───────┤ │6│41│Barry │ ├─┼──┼───────┤ │7│39│Stephen│ └─┴──┴───────┘

  dense rank competitors

┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │2│42│Jason │ ├─┼──┼───────┤ │2│42│Errol │ ├─┼──┼───────┤ │3│41│Garry │ ├─┼──┼───────┤ │3│41│Bernard│ ├─┼──┼───────┤ │3│41│Barry │ ├─┼──┼───────┤ │4│39│Stephen│ └─┴──┴───────┘

  ordinal rank competitors

┌─┬──┬───────┐ │1│44│Solomon│ ├─┼──┼───────┤ │2│42│Jason │ ├─┼──┼───────┤ │3│42│Errol │ ├─┼──┼───────┤ │4│41│Garry │ ├─┼──┼───────┤ │5│41│Bernard│ ├─┼──┼───────┤ │6│41│Barry │ ├─┼──┼───────┤ │7│39│Stephen│ └─┴──┴───────┘

  fractional rank competitors

┌───┬──┬───────┐ │1 │44│Solomon│ ├───┼──┼───────┤ │2.5│42│Jason │ ├───┼──┼───────┤ │2.5│42│Errol │ ├───┼──┼───────┤ │5 │41│Garry │ ├───┼──┼───────┤ │5 │41│Bernard│ ├───┼──┼───────┤ │5 │41│Barry │ ├───┼──┼───────┤ │7 │39│Stephen│ └───┴──┴───────┘</lang>

Java

Works with: Java version 8

<lang java>import java.util.*;

public class RankingMethods {

   final static String[] input = {"44 Solomon", "42 Jason", "42 Errol",
       "41 Garry", "41 Bernard", "41 Barry", "39 Stephen"};
   public static void main(String[] args) {
       int len = input.length;
       Map<String, int[]> map = new TreeMap<>((a, b) -> b.compareTo(a));
       for (int i = 0; i < len; i++) {
           String key = input[i].split("\\s+")[0];
           int[] arr;
           if ((arr = map.get(key)) == null)
               arr = new int[]{i, 0};
           arr[1]++;
           map.put(key, arr);
       }
       int[][] groups = map.values().toArray(new int[map.size()][]);
       standardRanking(len, groups);
       modifiedRanking(len, groups);
       denseRanking(len, groups);
       ordinalRanking(len);
       fractionalRanking(len, groups);
   }
   private static void standardRanking(int len, int[][] groups) {
       System.out.println("\nStandard ranking");
       for (int i = 0, rank = 0, group = 0; i < len; i++) {
           if (group < groups.length && i == groups[group][0]) {
               rank = i + 1;
               group++;
           }
           System.out.printf("%d %s%n", rank, input[i]);
       }
   }
   private static void modifiedRanking(int len, int[][] groups) {
       System.out.println("\nModified ranking");
       for (int i = 0, rank = 0, group = 0; i < len; i++) {
           if (group < groups.length && i == groups[group][0])
               rank += groups[group++][1];
           System.out.printf("%d %s%n", rank, input[i]);
       }
   }
   private static void denseRanking(int len, int[][] groups) {
       System.out.println("\nDense ranking");
       for (int i = 0, rank = 0; i < len; i++) {
           if (rank < groups.length && i == groups[rank][0])
               rank++;
           System.out.printf("%d %s%n", rank, input[i]);
       }
   }
   private static void ordinalRanking(int len) {
       System.out.println("\nOrdinal ranking");
       for (int i = 0; i < len; i++)
           System.out.printf("%d %s%n", i + 1, input[i]);
   }
   private static void fractionalRanking(int len, int[][] groups) {
       System.out.println("\nFractional ranking");
       float rank = 0;
       for (int i = 0, tmp = 0, group = 0; i < len; i++) {
           if (group < groups.length && i == groups[group][0]) {
               tmp += groups[group++][1];
               rank = (i + 1 + tmp) / 2.0F;
           }
           System.out.printf("%2.1f %s%n", rank, input[i]);
       }
   }

}</lang>

Standard ranking
1 44 Solomon
2 42 Jason
2 42 Errol
4 41 Garry
4 41 Bernard
4 41 Barry
7 39 Stephen

Modified ranking
1 44 Solomon
3 42 Jason
3 42 Errol
6 41 Garry
6 41 Bernard
6 41 Barry
7 39 Stephen

Dense ranking
1 44 Solomon
2 42 Jason
2 42 Errol
3 41 Garry
3 41 Bernard
3 41 Barry
4 39 Stephen

Ordinal ranking
1 44 Solomon
2 42 Jason
3 42 Errol
4 41 Garry
5 41 Bernard
6 41 Barry
7 39 Stephen

Fractional ranking
1,0 44 Solomon
2,5 42 Jason
2,5 42 Errol
5,0 41 Garry
5,0 41 Bernard
5,0 41 Barry
7,0 39 Stephen

jq

We assume the list of players and their scores has already been sorted, and that the list takes the form:

[ player1, score1, player2, score2, ...]

For the sake of brevity, only the ranks are printed. <lang jq>

  1. Ties share what would have been their first ordinal number

def standard_ranking:

 . as $raw
 | ([range(1;length;2) | $raw[.]]) as $scores
 | reduce range(1; $scores|length) as $i
     ([1]; if $scores[$i - 1] == $scores[$i] then . + [.[-1]]
           else . + [$i + 1]
           end) ;

def modified_ranking:

 # The helper function resolves [ranks, tentative]
 # by appending the ranks of the ties to "ranks"
 def resolve:
   (.[1] | length) as $length
   | if $length == 0 then .[0]
     else .[1][-1] as $max
          | .[0] + ( .[1] | map( $max) )
     end ;
 . as $raw
 | ([range(1;length;2) | $raw[.]]) as $scores
 | reduce range(1; $scores|length) as $i
     # state: [ranks, tentative]
     ([ [], [1] ];
      if $scores[$i - 1] == $scores[$i] then [.[0], .[1] + [ $i + 1 ]]
      else [ resolve,  [ $i + 1 ] ]
      end )
 | resolve ;

def dense_ranking: # next available

 . as $raw
 | ([range(1;length;2) | $raw[.]]) as $scores
 | reduce range(1; $scores|length) as $i
     ([1]; if $scores[$i - 1] == $scores[$i] then . + [.[-1]]
           else . + [ .[-1] + 1]
           end );

def ordinal_ranking: # unfair to some!

 [ range(1; 1 + length/2) ] ;

def fractional_ranking:

 # The helper function resolves [ranks, tentative]
 # by appending the averages of the tentative ranks to "ranks"
 def resolve:
   (.[1] | length) as $length
   | if $length == 0 then .[0]
     else (.[1] | add  / $length) as $avg
          | .[0] + ( .[1] | map( $avg) )
     end ;
 . as $raw
 | ([range(1;length;2) | $raw[.]]) as $scores
 | reduce range(1; $scores|length) as $i
     # state: [ranks, tentative]
     ([ [], [1] ];
      if $scores[$i - 1] == $scores[$i] then [.[0], .[1] + [ $i + 1 ]]
      else [ resolve,  [ $i + 1 ] ]
      end )
 | resolve ;</lang>Task<lang jq>def raw:
[
"Solomon",  44,
"Jason"  ,  42,
"Errol"  ,  42,
"Garry"  ,  41,
"Bernard",  41,
"Barry"  ,  41,
"Stephen",  39

] ;

def task:

"standard:   \( raw | standard_ranking)",
"modified:   \( raw | modified_ranking)",
"dense:      \( raw | dense_ranking)",
"ordinal:    \( raw | ordinal_ranking)",
"fractional: \( raw | fractional_ranking)" ;
 

task </lang>

Output:
standard:   [1,2,2,4,4,4,7]
modified:   [1,3,3,6,6,6,7]
dense:      [1,2,2,3,3,3,4]
ordinal:    [1,2,3,4,5,6,7]
fractional: [1,2.5,2.5,5,5,5,7]

Perl 6

<lang perl6>my @scores =

   Solomon => 44,
   Jason   => 42,
   Errol   => 42,
   Garry   => 41,
   Bernard => 41,
   Barry   => 41,
   Stephen => 39;

sub tiers (@s) { @s.classify(*.value).pairs.sort.reverse.map: { [.value».key] } }

sub standard (@s) {

   my $rank = 1;
   gather for tiers @s -> @players {

take $rank => @players; $rank += @players;

   }

}

sub modified (@s) {

   my $rank = 0;
   gather for tiers @s -> @players {

$rank += @players; take $rank => @players;

   }

}

sub dense (@s) { tiers(@s).map: { ++$_ => @^players } }

sub ordinal (@s) { @s.map: ++$_ => *.key }

sub fractional (@s) {

   my $rank = 1;
   gather for tiers @s -> @players {

my $beg = $rank; my $end = $rank += @players; take [+]($beg ..^ $end) / @players => @players;

   }

}

say "Standard:"; .say for standard @scores; say "\nModified:"; .say for modified @scores; say "\nDense:"; .say for dense @scores; say "\nOrdinal:"; .say for ordinal @scores; say "\nFractional:"; .say for fractional @scores;</lang>

Output:
Standard:
1 => ["Solomon"]
2 => ["Jason", "Errol"]
4 => ["Garry", "Bernard", "Barry"]
7 => ["Stephen"]

Modified:
1 => ["Solomon"]
3 => ["Jason", "Errol"]
6 => ["Garry", "Bernard", "Barry"]
7 => ["Stephen"]

Dense:
1 => ["Solomon"]
2 => ["Jason", "Errol"]
3 => ["Garry", "Bernard", "Barry"]
4 => ["Stephen"]

Ordinal:
1 => "Solomon"
2 => "Jason"
3 => "Errol"
4 => "Garry"
5 => "Bernard"
6 => "Barry"
7 => "Stephen"

Fractional:
1.0 => ["Solomon"]
2.5 => ["Jason", "Errol"]
5.0 => ["Garry", "Bernard", "Barry"]
7.0 => ["Stephen"]

Python

<lang python>def mc_rank(iterable, start=1):

   """Modified competition ranking"""
   lastresult, fifo = None, []
   for n, item in enumerate(iterable, start-1):
       if item[0] == lastresult:
           fifo += [item]
       else:
           while fifo:
               yield n, fifo.pop(0)
           lastresult, fifo = item[0], fifo + [item]
   while fifo:
       yield n+1, fifo.pop(0)


def sc_rank(iterable, start=1):

   """Standard competition ranking"""
   lastresult, lastrank = None, None
   for n, item in enumerate(iterable, start):
       if item[0] == lastresult:
           yield lastrank, item
       else:
           yield n, item
           lastresult, lastrank = item[0], n


def d_rank(iterable, start=1):

   """Dense ranking"""
   lastresult, lastrank = None, start - 1,
   for item in iterable:
       if item[0] == lastresult:
           yield lastrank, item
       else:
           lastresult, lastrank = item[0], lastrank + 1
           yield lastrank, item


def o_rank(iterable, start=1):

   """Ordinal  ranking"""
   yield from enumerate(iterable, start)


def f_rank(iterable, start=1):

   """Fractional ranking"""
   last, fifo = None, []
   for n, item in enumerate(iterable, start):
       if item[0] != last:
           if fifo:
               mean = sum(f[0] for f in fifo) / len(fifo)
               while fifo:
                   yield mean, fifo.pop(0)[1]
       last = item[0]
       fifo.append((n, item))
   if fifo:
       mean = sum(f[0] for f in fifo) / len(fifo)
       while fifo:
           yield mean, fifo.pop(0)[1]


if __name__ == '__main__':

   scores = [(44, 'Solomon'),
             (42, 'Jason'),
             (42, 'Errol'),
             (41, 'Garry'),
             (41, 'Bernard'),
             (41, 'Barry'),
             (39, 'Stephen')]
   print('\nScores to be ranked (best first):')
   for s in scores:
       print('        %2i %s' % (s ))
   for ranker in [sc_rank, mc_rank, d_rank, o_rank, f_rank]:
       print('\n%s:' % ranker.__doc__)
       for rank, score in ranker(scores):
           print('  %3g, %r' % (rank, score))</lang>
Output:
Scores to be ranked (best first):
        44 Solomon
        42 Jason
        42 Errol
        41 Garry
        41 Bernard
        41 Barry
        39 Stephen

Standard competition ranking:
    1, (44, 'Solomon')
    2, (42, 'Jason')
    2, (42, 'Errol')
    4, (41, 'Garry')
    4, (41, 'Bernard')
    4, (41, 'Barry')
    7, (39, 'Stephen')

Modified competition ranking:
    1, (44, 'Solomon')
    3, (42, 'Jason')
    3, (42, 'Errol')
    6, (41, 'Garry')
    6, (41, 'Bernard')
    6, (41, 'Barry')
    7, (39, 'Stephen')

Dense ranking:
    1, (44, 'Solomon')
    2, (42, 'Jason')
    2, (42, 'Errol')
    3, (41, 'Garry')
    3, (41, 'Bernard')
    3, (41, 'Barry')
    4, (39, 'Stephen')

Ordinal  ranking:
    1, (44, 'Solomon')
    2, (42, 'Jason')
    3, (42, 'Errol')
    4, (41, 'Garry')
    5, (41, 'Bernard')
    6, (41, 'Barry')
    7, (39, 'Stephen')

Fractional ranking:
    1, (44, 'Solomon')
  2.5, (42, 'Jason')
  2.5, (42, 'Errol')
    5, (41, 'Garry')
    5, (41, 'Bernard')
    5, (41, 'Barry')
    7, (39, 'Stephen')

Racket

<lang racket>#lang racket

Tim-brown 2014-09-11
produces a ranking according to ranking function
rfn
INPUT
lst
(list (score . details))
rfn
(length-scores)
-> (values
ranks-added  ; how many ranks to add for the next iteration
(idx . rest -> rank-offset)) ; function that produces the rank for the idx^th element
; in the scoring (arity must be >= 1)

(define (rank-list all-scores rfn)

 (let loop ((rank-0 0) (lst (sort all-scores > #:key car)) (acc empty))
   (cond
     [(null? lst) acc]
     [else
      (define 1st-score (caar lst))
      (define (ties-with-1st? cand) (= (car cand) 1st-score))
      (define-values (tied unranked) (splitf-at lst ties-with-1st?))
      ;; all ranking functions should properly handle a singleton tied list
      (define tied-len (length tied))
      (define tied? (> tied-len 1))
      (define-values (ranks-added rank-offset-fn) (rfn tied-len))
      (define ranked-tied (for/list ((t (in-list tied)) (i (in-naturals 1)))
                            (list* tied? (+ rank-0 (rank-offset-fn i)) t)))
      (loop (+ ranks-added rank-0) unranked (append acc ranked-tied))])))
Ties share what would have been their first ordinal number

(define (rank-function:Standard l)

 (values l (thunk* 1)))
Ties share what would have been their last ordinal number

(define (rank-function:Modified l)

 (values l (thunk* l)))
Ties share the next available integer

(define (rank-function:Dense l)

 (values 1 (thunk* 1)))
Competitors take the next available integer. Ties are not treated otherwise

(define (rank-function:Ordinal l)

 (values l (λ (n . _) n)))
Ties share the mean of what would have been their ordinal numbers

(define (rank-function:Fractional l)

 (values l (thunk* (/ (+ l 1) 2))))

(define score-board

 '((44 . Solomon)
   (42 . Jason)
   (42 . Errol)
   (41 . Garry)
   (41 . Bernard)
   (41 . Barry)
   (39 . Stephen)))

(define format-number

 (match-lambda
   [(? integer? i) i]
   [(and f (app numerator n) (app denominator d))
    (define-values (q r) (quotient/remainder n d))
    (format "~a ~a/~a" q r d)]))

(for ((fn (list

          rank-function:Standard
          rank-function:Modified
          rank-function:Dense
          rank-function:Ordinal
          rank-function:Fractional)))
 (printf "Function: ~s~%" fn)
 (for ((r (in-list (rank-list score-board fn))))
   (printf "~a ~a\t~a\t~a~%"
           (if (car r) "=" " ")
           (format-number (cadr r))
           (caddr r)
           (cdddr r)))
 (newline))</lang>
Output:
Function: #<procedure:rank-function:Standard>
  1	44	Solomon
= 2	42	Jason
= 2	42	Errol
= 4	41	Garry
= 4	41	Bernard
= 4	41	Barry
  7	39	Stephen

Function: #<procedure:rank-function:Modified>
  1	44	Solomon
= 3	42	Jason
= 3	42	Errol
= 6	41	Garry
= 6	41	Bernard
= 6	41	Barry
  7	39	Stephen

Function: #<procedure:rank-function:Dense>
  1	44	Solomon
= 2	42	Jason
= 2	42	Errol
= 3	41	Garry
= 3	41	Bernard
= 3	41	Barry
  4	39	Stephen

Function: #<procedure:rank-function:Ordinal>
  1	44	Solomon
= 2	42	Jason
= 3	42	Errol
= 4	41	Garry
= 5	41	Bernard
= 6	41	Barry
  7	39	Stephen

Function: #<procedure:rank-function:Fractional>
  1	44	Solomon
= 2 1/2	42	Jason
= 2 1/2	42	Errol
= 5	41	Garry
= 5	41	Bernard
= 5	41	Barry
  7	39	Stephen

Ruby

<lang ruby>ar = "44 Solomon 42 Jason 42 Errol 41 Garry 41 Bernard 41 Barry 39 Stephen".lines.map{|line| line.split}

grouped = ar.group_by{|pair| pair.delete_at(0).to_i} s_rnk = 1 m_rnk = o_rnk = f_rnk = 0 puts "stand.\tmod.\tdense\tord.\tfract."

grouped.each.with_index do |(score, names), i|

 d_rnk = i + 1 
 m_rnk += names.flatten!.size
 f_rnk = (s_rnk + m_rnk)/2.0
 names.each do |name|
   o_rnk += 1
   puts "#{s_rnk}\t#{m_rnk}\t#{d_rnk}\t#{o_rnk}\t#{f_rnk.to_s.gsub(".0","")}\t#{score} #{name}"
 end
 s_rnk += names.size

end</lang>

Output:
stand.	mod.	dense	ord.	fract.
1	1	1	1	1	44 Solomon
2	3	2	2	2.5	42 Jason
2	3	2	3	2.5	42 Errol
4	6	3	4	5	41 Garry
4	6	3	5	5	41 Bernard
4	6	3	6	5	41 Barry
7	7	4	7	7	39 Stephen

Scala

This example uses a type-safe singly-linked object model with no mutable state variables, which makes it longer than the Ruby version but demonstrates object-oriented functional programming style. <lang Scala>object RankingMethods extends App {

   case class Score(score: Int, name: String) // incoming data
   case class Rank[Precision](rank: Precision, names: List[String]) // outgoing results (can be int or double)
   case class State[Precision](n: Int, done: List[Rank[Precision]]) { // internal state, no mutable variables
       def next(n: Int, next: Rank[Precision]) = State(n, next :: done)
   }
   def grouped[Precision](list: List[Score]) = // group names together by score, with highest first
       (scala.collection.immutable.TreeMap[Int, List[Score]]() ++ list.groupBy(-_.score))
       .values.map(_.map(_.name)).foldLeft(State[Precision](1, Nil)) _
   // Ranking methods:
   def rankStandard(list: List[Score]) =
       grouped[Int](list){case (state, names) => state.next(state.n+names.length, Rank(state.n, names))}.done.reverse
   def rankModified(list: List[Score]) =
       rankStandard(list).map(r => Rank(r.rank+r.names.length-1, r.names))
   def rankDense(list: List[Score]) =
       grouped[Int](list){case (state, names) => state.next(state.n+1, Rank(state.n, names))}.done.reverse
   def rankOrdinal(list: List[Score]) =
       list.zipWithIndex.map{case (score, n) => Rank(n+1, List(score.name))}
   def rankFractional(list: List[Score]) =
       rankStandard(list).map(r => Rank((2*r.rank+r.names.length-1.0)/2, r.names))
   // Tests:
   def parseScores(s: String) = s split "\\s+" match {case Array(s,n) => Score(s.toInt, n)}
   val test = List("44 Solomon", "42 Jason", "42 Errol", "41 Garry", "41 Bernard", "41 Barry", "39 Stephen").map(parseScores)
   println("Standard:")
   println(rankStandard(test) mkString "\n")
   println("\nModified:")
   println(rankModified(test) mkString "\n")
   println("\nDense:")
   println(rankDense(test) mkString "\n")
   println("\nOrdinal:")
   println(rankOrdinal(test) mkString "\n")
   println("\nFractional:")
   println(rankFractional(test) mkString "\n")

}</lang>

Output:
Standard:
Rank(1,List(Solomon))
Rank(2,List(Jason, Errol))
Rank(4,List(Garry, Bernard, Barry))
Rank(7,List(Stephen))

Modified:
Rank(1,List(Solomon))
Rank(3,List(Jason, Errol))
Rank(6,List(Garry, Bernard, Barry))
Rank(7,List(Stephen))

Dense:
Rank(1,List(Solomon))
Rank(2,List(Jason, Errol))
Rank(3,List(Garry, Bernard, Barry))
Rank(4,List(Stephen))

Ordinal:
Rank(1,List(Solomon))
Rank(2,List(Jason))
Rank(3,List(Errol))
Rank(4,List(Garry))
Rank(5,List(Bernard))
Rank(6,List(Barry))
Rank(7,List(Stephen))

Fractional:
Rank(1.0,List(Solomon))
Rank(2.5,List(Jason, Errol))
Rank(5.0,List(Garry, Bernard, Barry))
Rank(7.0,List(Stephen))

Tcl

<lang tcl>proc rank {rankingMethod sortedList} {

   # Extract the groups in the data (this is pointless for ordinal...)
   set s [set group [set groups {}]]
   foreach {score who} $sortedList {

if {$score != $s} { lappend groups [llength $group] set s $score set group {} } lappend group $who

   }
   lappend groups [llength $group]
   # Construct the rankings; note that we have a zero-sized leading group
   set n 1; set m 0
   foreach g $groups {

switch $rankingMethod { standard { lappend result {*}[lrepeat $g $n] incr n $g } modified { lappend result {*}[lrepeat $g [incr m $g]] } dense { lappend result {*}[lrepeat $g $m] incr m } ordinal { for {set i 0} {$i < $g} {incr i} { lappend result [incr m] } } fractional { set val [expr {($n + [incr n $g] - 1) / 2.0}] lappend result {*}[lrepeat $g [format %g $val]] } }

   }
   return $result

}

set data {

   44 Solomon
   42 Jason
   42 Errol
   41 Garry
   41 Bernard
   41 Barry
   39 Stephen

} foreach method {standard modified dense ordinal fractional} {

   puts "Using method '$method'...\n  Rank\tScore\tWho"
   foreach rank [rank $method $data] {score who} $data {

puts " $rank\t$score\t$who"

   }

}</lang>

Output:
Using method 'standard'...
  Rank	Score	Who
  1	44	Solomon
  2	42	Jason
  2	42	Errol
  4	41	Garry
  4	41	Bernard
  4	41	Barry
  7	39	Stephen
Using method 'modified'...
  Rank	Score	Who
  1	44	Solomon
  3	42	Jason
  3	42	Errol
  6	41	Garry
  6	41	Bernard
  6	41	Barry
  7	39	Stephen
Using method 'dense'...
  Rank	Score	Who
  1	44	Solomon
  2	42	Jason
  2	42	Errol
  3	41	Garry
  3	41	Bernard
  3	41	Barry
  4	39	Stephen
Using method 'ordinal'...
  Rank	Score	Who
  1	44	Solomon
  2	42	Jason
  3	42	Errol
  4	41	Garry
  5	41	Bernard
  6	41	Barry
  7	39	Stephen
Using method 'fractional'...
  Rank	Score	Who
  1	44	Solomon
  2.5	42	Jason
  2.5	42	Errol
  5	41	Garry
  5	41	Bernard
  5	41	Barry
  7	39	Stephen

zkl

<lang zkl>fcn group(scores){ // group like scores into one list --> list of lists

  sink:=List();
  scores.reduce('wrap(ps,sn,buf){
     if(sn[0]!=ps){ sink.append(buf.copy()); buf.clear(); }
     buf+sn;
     sn[0];
  },scores[0][0],buf:=List());
  sink.append(buf);

} fcn print(list,rank){

  list.apply2('wrap(sn){ "%2s: %s (%d)".fmt(rank,sn[1],sn[0]):println(_); });

} fcn rankViaStandard(scores){

  rank:=1;
  foreach group in (group(scores)){ print(group,rank); rank+=group.len(); }

} fcn rankViaModified(scores){

  rank:=0;
  foreach group in (group(scores)){ rank+=group.len(); print(group,rank); }

} fcn rankViaDense(scores){

  rank:=1;
  foreach group in (group(scores)){ print(group,rank); rank+=1; }

} fcn rankViaOrdinal(scores){

  scores.apply2('wrap(sn,rr){ "%2s: %s (%d)".fmt(rr.inc(),sn[1],sn[0]):println(_); },Ref(1));

} fcn rankViaFractional(scores){

  rank:=1;
  foreach group in (group(scores)){
     n:=group.len(); r:=rank.reduce(n,'+,0.0)/n; rank+=n;
     print(group,"%5.2f".fmt(r));
  }

}</lang> <lang zkl> // these are sorted!?! scores:=T(T(44,"Solomon"), T(42,"Jason"), T(42,"Errol"), T(41,"Garry"),

       T(41,"Bernard"),T(41,"Barry"),T(39,"Stephen"),);

"Standard:" .println(); rankViaStandard(scores); "Modified:" .println(); rankViaModified(scores); "Dense:" .println(); rankViaDense(scores); "Ordinal:" .println(); rankViaOrdinal(scores); "Fractional:".println(); rankViaFractional(scores);</lang>

Output:
Standard:
 1: Solomon (44)
 2: Jason (42)
 2: Errol (42)
 4: Garry (41)
 4: Bernard (41)
 4: Barry (41)
 7: Stephen (39)
Modified:
 1: Solomon (44)
 3: Jason (42)
 3: Errol (42)
 6: Garry (41)
 6: Bernard (41)
 6: Barry (41)
 7: Stephen (39)
Dense:
 1: Solomon (44)
 2: Jason (42)
 2: Errol (42)
 3: Garry (41)
 3: Bernard (41)
 3: Barry (41)
 4: Stephen (39)
Ordinal:
 1: Solomon (44)
 2: Jason (42)
 3: Errol (42)
 4: Garry (41)
 5: Bernard (41)
 6: Barry (41)
 7: Stephen (39)
Fractional:
 1.00: Solomon (44)
 2.50: Jason (42)
 2.50: Errol (42)
 5.00: Garry (41)
 5.00: Bernard (41)
 5.00: Barry (41)
 7.00: Stephen (39)