The classify routine

Combined from primary sources listed below.

In class List ( Type/List )§

See primary docmentation in context for routine classify.

routine classify§

multi method classify($test, :$into!, :&as)
multi method classify($test, :&as)
multi        classify($test, +items, :$into!, *%named )
multi        classify($test, +items, *%named )

Transforms a list of values into a hash representing the classification of those values; each hash key represents the classification for one or more of the incoming list values, and the corresponding hash value contains an array of those list values classified into the category of the associated key. $test will be an expression that will produce the hash keys according to which the elements are going to be classified.

Example:

say classify { $_ %% 2 ?? 'even' !! 'odd' }, (1, 7, 6, 3, 2);
# OUTPUT: «{even => [6 2], odd => [1 7 3]}␤»
say ('hello', 1, 22/7, 42, 'world').classify: { .Str.chars };
# OUTPUT: «{1 => [1], 2 => [42], 5 => [hello world], 8 => [3.142857]}␤»

It can also take :as as a named parameter, transforming the value before classifying it:

say <Innie Minnie Moe>.classify( { $_.chars }, :as{ lc $_ });
# OUTPUT: «{3 => [moe], 5 => [innie], 6 => [minnie]}␤»

This code is classifying by number of characters, which is the expression that has been passed as $test parameter, but the :as block lowercases it before doing the transformation. The named parameter :into can also be used to classify into a newly defined variable:

<Innie Minnie Moe>.classify( { $_.chars }, :as{ lc $_ }, :into( my %words{Int} ) );
say %words# OUTPUT: «{3 => [moe], 5 => [innie], 6 => [minnie]}␤»

We are declaring the scope of %words{Int} on the fly, with keys that are actually integers; it gets created with the result of the classification.

In role Baggy ( Type/Baggy )§

See primary docmentation in context for method classify.

method classify-list§

multi method classify-list(&mapper, *@list --> Baggy:D)
multi method classify-list(%mapper, *@list --> Baggy:D)
multi method classify-list(@mapper, *@list --> Baggy:D)

Populates a mutable Baggy by classifying the possibly-empty @list of values using the given mapper. The @list cannot be lazy.

say BagHash.new.classify-list: { $_ %% 2 ?? 'even' !! 'odd' }, ^10;
# OUTPUT: BagHash(even(5) odd(5))

my @mapper = <zero one two three four five>;
say MixHash.new.classify-list: @mapper, 1, 2, 3, 4, 4, 6;
# OUTPUT: MixHash((Any) two three four(2) one)

The mapper can be a Callable that takes a single argument, an Associative, or an Iterable. With Associative and an Iterable mappers, the values in the @list represent the key and index of the mapper's value respectively. A Callable mapper will be executed once per each item in the @list, with that item as the argument and its return value will be used as the mapper's value.

The mapper's value is used as the key of the Baggy that will be incremented by 1. See .categorize-list if you wish to classify an item into multiple categories at once.

Note: unlike the Hash's .classify-list, returning an Iterable mapper's value will throw, as Baggy types do not support nested classification. For the same reason, Baggy's .classify-list does not accept :&as parameter.

In class Any ( Type/Any )§

See primary docmentation in context for routine classify.

routine classify§

multi method classify()
multi method classify(Whatever)
multi method classify($test, :$into!, :&as)
multi method classify($test, :&as)
multi        classify($test, +items, :$into!, *%named )
multi        classify($test, +items, *%named )

The first form will always fail. The second form classifies on the identity of the given object, which usually only makes sense in combination with the :&as argument.

The rest include a $test argument, which is a function that will return a scalar for every input; these will be used as keys of a hash whose values will be arrays with the elements that output that key for the test function.

my @years = (2003..2008).map( { Date.new$_~"-01-01" ) } );
@years.classify*.is-leap-year , into => my %leap-years );
say %leap-years;
# OUTPUT: «{False => [2003-01-01 2005-01-01 2006-01-01 2007-01-01],
# True => [2004-01-01 2008-01-01]}␤»

Similarly to .categorize, elements can be normalized by the Callable passed with the :as argument, and it can use the :into named argument to pass a Hash the results will be classified into; in the example above, it's defined on the fly.

From version 6.d, .classify will also work with Junctions.

Support for using Whatever as the test was added in Rakudo compiler version 2023.02.

In class Hash ( Type/Hash )§

See primary docmentation in context for method classify.

method classify-list§

multi method classify-list(&mapper, *@list, :&as --> Hash:D)
multi method classify-list(%mapper, *@list, :&as --> Hash:D)
multi method classify-list(@mapper, *@list, :&as --> Hash:D)

Populates a Hash by classifying the possibly-empty @list of values using the given mapper, optionally altering the values using the :&as Callable. The @list cannot be lazy.

The mapper can be a Callable that takes a single argument, an Associative, or an Iterable; this Callable is guaranteed to be called only once per item. With Associative and an Iterable mappers, the values in the @list represent the key and index of the mapper's value respectively. A Callable mapper will be executed once per each item in the @list, with that item as the argument and its return value will be used as the mapper's value.

Simple classification§

In simple classification mode, each mapper's value is any non-Iterable and represents a key to classify @list's item under:

say % .classify-list: { $_ %% 2 ?? 'even' !! 'odd' }, ^10;
# OUTPUT: «{even => [0 2 4 6 8], odd => [1 3 5 7 9]}␤»

my @mapper = <zero one two three four five>;
my %hash = foo => 'bar';
say %hash.classify-list: @mapper, 1, 2, 3, 4, 4;
# OUTPUT: «{foo => bar, four => [4 4], one => [1], three => [3], two => [2]}␤»

The mapper's value is used as the key of the Hash to which the @list's item will be pushed. See .categorize-list if you wish to classify an item into multiple categories at once.

Multi-level classification§

In multi-level classification mode, each mapper's value is an Iterable that represents a tree of hash keys to classify @list's item under:

say % .classify-list: {
    [
        (.is-prime ?? 'prime' !! 'non-prime'),
        ($_ %% 2   ?? 'even'  !! 'odd'      ),
    ]
}, ^10;
# OUTPUT:
# {
# non-prime => {
# even => [0 4 6 8],
# odd => [1 9]
# },
# prime => {
# even => [2],
# odd => [3 5 7]
# }
# }

In the case we are using Iterables and not Callables, each of those Iterables must have the same number of elements, or the method will throw an exception. This restriction exists to avoid conflicts when the same key is a leaf of one value's classification but a node of another value's classification.

my @mapper = [['1a','1b','1c'],['2a','2b','2c'],['3a','3b','3c']];
say % .classify-list: @mapper, 1,2,1,1,2,0;
# OUTPUT: «{1a => {1b => {1c => [0]}}, 2a => {2b => {2c => [1 1 1]}}, 3a => {3b => {3c => [2 2]}}}␤»

Every element of the array represents a different level in the tree, with the elements of the list that is being mapped used as index, and the elements of the mapper array used as keys to the different levels. So 0 selects the first sub-array and then the subsequent levels are built by running over the rest of the elements of that sub-array.

my @mapper = [['1a','1b'],['2a','2b'],['3a','3b']];
say % .classify-list: @mapper, 1,0,1,1,1,0,2;
# OUTPUT: «{1a => {1b => [0 0]}, 2a => {2b => [1 1 1 1]}, 3a => {3b => [2]}}␤»

From version 6.d, trying to use Iterables of different size will throw an error:

my @mapper = [<1a 1b>, <2a 2b 2fail>];
say % .classify-list: @mapper, 1,0,1,1,1,0;
# OUTPUT: «mapper on classify-list computed to an item with different number
# of elements in it than previous items, which cannot be used because all
# values need to have the same number of elements. Mixed-level classification
# is not supported.␤ in block <unit>…»

:&as value modifier§

If :&as Callable argument is specified, it will be called once per each item of @list, with the value as the argument, and its return value will be used instead of the original @list's item:

say % .classify-list: :as{"Value is $_"}, { $_ %% 2 ?? 'even' !! 'odd' }, ^5;
# OUTPUT (slightly altered manually, for clarity):
# {
# even => ['Value is 0', 'Value is 2', 'Value is 4'],
# odd => ['Value is 1', 'Value is 3']
# }