The categorize routine

Combined from primary sources listed below.

In class Any ( Type/Any )§

See primary docmentation in context for routine categorize.

routine categorize§

multi method categorize()
multi method categorize(Whatever)
multi method categorize($test, :$into!, :&as)
multi method categorize($test, :&as)
multi        categorize($test, +items, :$into!, *%named )
multi        categorize($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.

In its simplest form, it uses a $test whose result will be used as a key; the values of the key will be an array of the elements that produced that key as a result of the test.

say (1..13).categorize* %% 3);
say categorize* %% 3, 1..13)
# OUTPUT: «{False => [1 2 4 5 7 8 10 11 13], True => [3 6 9 12]}␤»

The :as argument will normalize before categorizing

say categorize* %% 3, -5..5, as => &abs )
# OUTPUT: «{False => [5 4 2 1 1 2 4 5], True => [3 0 3]}␤»

The $into associative argument can be used to put the result instead of returning a new Hash.

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

The function used to categorize can return an array indicating all possible bins their argument can be put into:

sub divisible-by( Int $n --> Array(Seq) ) {
    gather {
        for <2 3 5 7> {
            take $_ if $n %% $_;
        }
    }
}

say (3..13).categorize&divisible-by );
# OUTPUT:
# «{2 => [4 6 8 10 12], 3 => [3 6 9 12], 5 => [5 10], 7 => [7]}␤»

In this case, every number in the range is classified in as many bins as it can be divided by.

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

In role Baggy ( Type/Baggy )§

See primary docmentation in context for method categorize.

method categorize-list§

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

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

say BagHash.new.categorize-list: {
    gather {
        take 'largish' if $_ > 5;
        take .is-prime ?? 'prime' !! 'non-prime';
        take $_ %% 2   ?? 'even'  !! 'odd';
    }
}, ^10;
# OUTPUT: BagHash(largish(4) even(5) non-prime(6) prime(4) odd(5))

my %mapper = :sugar<sweet white>, :lemon<sour>, :cake('sweet', 'is-a-lie');
say MixHash.new.categorize-list: %mapper, <sugar lemon cake>;
# OUTPUT: MixHash(is-a-lie sour white sweet(2))

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 a possibly-empty list of keys of the Baggy that will be incremented by 1.

Note: unlike the Hash's .categorize-list, returning a list of Iterables as mapper's value will throw, as Baggy types do not support nested categorization. For the same reason, Baggy's .categorize-list does not accept :&as parameter.

In class List ( Type/List )§

See primary docmentation in context for routine categorize.

routine categorize§

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

These methods are directly inherited from Any; see Any.list for more examples.

This routine transforms a list of values into a hash representing the categorizations of those values according to $test, which is called once for every element in the list; each hash key represents one possible categorization for one or more of the incoming list values, and the corresponding hash value contains an array of those list values categorized by the $test, acting like a mapper, into the category of the associated key.

Note that, unlike classify, which assumes that the return value of the mapper is a single value, categorize always assumes that the return value of the mapper is a list of categories that are appropriate to the current value.

Example:

sub mapper(Int $ireturns List {
    $i %% 2 ?? 'even' !! 'odd',
    $i.is-prime ?? 'prime' !! 'not prime'
}
say categorize &mapper, (1, 7, 6, 3, 2);
# OUTPUT: «{even => [6 2], not prime => [1 6], odd => [1 7 3], prime => [7 3 2]}␤»

In class Hash ( Type/Hash )§

See primary docmentation in context for method categorize.

method categorize-list§

multi method categorize-list(&mapper, *@list, :&as --> Hash:D)
multi method categorize-list(%mapper, *@list, :&as --> Hash:D)
multi method categorize-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. 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 categorization§

The mapper's value is expected to be a possibly empty list of non-Iterables that represent categories to place the value into:

say % .categorize-list: {
    gather {
        take 'prime'   if .is-prime;
        take 'largish' if $_ > 5;
        take $_ %% 2 ?? 'even' !! 'odd';
    }
}, ^10;

# OUTPUT:
# {
# prime => [2 3 5 7]
# even => [0 2 4 6 8],
# odd => [1 3 5 7 9],
# largish => [6 7 8 9],
# }

Notice how some items, e.g. 6 and 7, are present in several categories.

Multi-level categorization§

In multi-level categorization, the categories produced by the mapper are Iterables and categorization combines features of classify, by producing nested hashes of classifications for each category.

say % .categorize-list: {
    [
        $_ > 5    ?? 'largish' !! 'smallish',
        .is-prime ?? 'prime'   !! 'non-prime',
    ],
}, ^10;

# OUTPUT:
# {
# largish => {
# non-prime => [6 8 9],
# prime => [7]
# },
# smallish => {
# non-prime => [0 1 4],
# prime => [2 3 5]
# }
# }

The mapper in the snippet above produces a single-item list (note the significant trailing comma) with a two-item Array in it. The first item in that array indicates the first level of classification: the largish/smallish categories the routine produces. The second item in that array indicates further levels of classification, in our case the classification into prime/non-prime inside of each category.

NOTE:: every Iterables category 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.

:&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 % .categorize-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']
# }