Raku Land

Ranker

cpan:UZLUISF

NAME

Ranker - a module to rank a list of elements.

DESCRIPTION

This is a module for ranking a list/array of scorable elements using various ranking strategies written in Raku.

It's mostly based on Ilya Scharrenbroich's Ruby library by the same name.

INSTALLATION

Either:

SYNOPSIS

Default Ranking

Default ranking will assume values are numeric and rank them in their descending order. For example, a score of 100 is higher than a score of 50 and thus appears before as in [100, 50].

use Ranker;

my @scores = 1, 1, 2, 3, 3, 1, 4, 4, 5, 6, 8, 1, 0, 8;

my $rankings = Ranker::rank(@scores);
put $rankings.elems; #=> 8

my $ranking = $rankings[0];
given $ranking {
    .rank       .put; #=> 1
    .score      .put; #=> 8
    .rankables  .put; #=> [8, 8]
    .percentile .put; #=> 100
    .z-score    .put; #=> 1.83921346366645
}

Custom Ranking by Block

Custom ranking allows for ranking of objects by using an anonymous subroutine which can created with sub, with a pointy block or with a block.

class Player {
    has Int $.score;
}

my @players = (0, 100, 1000, 25).map: Player.new(score => *);
my (&score, $rankings);

# Explicit:
&score = -> $player { $player.score };
$rankings = Ranker::rank(@players, by => &score);

# Still more explicit:
&score = sub( Player:D $player ) { $player.score };
$rankings = Ranker::rank(@players, by => &score);

# Same thing but more succint: 
$rankings = Ranker::rank(@players, by => { $^player.score });

In some cases, objects need to be ranked by score in ascending order. For instance, if you were ranking golf players. In this case, 75 is higher than a score of 100 and thus appears before as in [75, 100].

class GolfPlayer is Player { }

my @golfplayers = (72, 100, 138, 54).map: GolfPlayer.new(score => *);
my $rankings = Ranker::rank(
    @golfplayers,              # Rankable values
    by => -> gp { $gp.score }, # Block to rank by
    :asc                       # Use ascending order
);

Ranking Strategies

Ranker provides several ranking strategies, which are mostly based on the Wikipedia entry for ranking. Strategies can be passed in as an option to the Ranker::rank subroutine.

my $rankings = Ranker::rank(
    @players,
    by       => { $^p.score },
    strategy => 'ordinal',
);

The possible string values for the default strategies are: 'standard', 'modified', 'ordinal', 'dense', and 'fractional'.

Standard Competition Ranking ("1224" ranking)

This is the default ranking strategy used by Ranker. For more info, see the Wikipedia entry on Standard Competition Ranking.

my $rankings = Ranker::rank(
    @players,
    by       => { $^p.score },
    strategy => 'standard',
);

Modified Competition Ranking ("1334" ranking)

For more info, see the Wikipedia entry on Modified Competition Ranking.

my $rankings = Ranker::rank(
    @players,
    by       => { $^p.score },
    strategy => 'modified',
);

Dense Ranking ("1223" ranking)

For more info, see the Wikipedia entry on Dense Ranking

my $rankings = Ranker::rank(
    @players,
    by       => { $^p.score },
    strategy => 'dense',
);

Ordinal Ranking ("1234" ranking)

For more info, see the Wikipedia entry on Ordinal Ranking.

my $rankings = Ranker::rank(
    @players,
    by       => { $^p.score },
    strategy => 'ordinal',
);

Fractional Ranking ("1 2.5 2.5 4" ranking)

For more info, see the Wikipedia entry on Fractional Ranking.

my $rankings = Ranker::rank(
    @players,
    by       => { $^p.score },
    strategy => 'fractional',
);

Custom Strategies

Ranker allows you to write your own strategies and supply them to the Ranker::rank subroutine. To do this, you must compose the Strategy role into a class and use ::() to interpolate the class name into a package or variable name. Strategy is the role from which the default «Strategy» classes are composed.

use Ranker;
use Ranker::Strategies;

# Composing Strategy into a class
class MyCustomStrategy does Strategy {

  method execute {
    # You must implement this method ;-)
  }

}

my $rankings = Ranker::rank(
    @players,
    by       => -> $p { $p.score },
    strategy => ::('MyCustomStrategy') # Passing the package-interpolated class name
);

By default, only the Strategy role is exported with use Ranker::Strategies, which must be composed before it's used. To export a default strategy, you must specify it with a use statement. For instance, use Ranker::Strategies :dense exports Strategies::Dense which can then be instantiated:

use Ranker::Strategies :dense;

my @scores = 44, 42, 42, 43;
my $ds = Dense.new: rankables => @scores, options => { :asc };

ROUTINES

Ranker

By default, values are assumed to be numeric and ranked in descending order using the standard-competition ranking strategy. Thus, the following two calls achieve the same thing:

Others

Run p6doc on Ranker::Ranking, Ranker::Rankings and Ranker::Strategies to get the methods made available by Ranking, Rankings and Strategy respectively.

AUTHOR

Luis F. Uceta

LICENSE

Artistic License 2.0