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:
from CPAN:
zef install Ranker
from local directory:
git clone git@gitlab.com:uzluisf/ranker.gitor download itzef install path/to/ranker/
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
Ranker::rank(@rankables, *%options)@rankables- list of elements to be ranked%options- key-value pairs to specify a custom ranking withby(e.g.,by => { $^value }), a sorting order withasc(e.g.,asc => True) and strategy to be used withstrategy(e.g.,strategy => 'dense').
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:
Ranker::rank(@values);Ranker::rank(@values, :by({ $^value }), :asc(False), :strategy('standard'));
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