### ----------------------------------------------------
### -- List::MoreUtils
### -- Licenses: Artistic-2.0
### -- Authors: Elizabeth Mattijsen
### -- File: lib/List/MoreUtils.rakumod
### ----------------------------------------------------
### -- Chunk 2 of 2
1,2,3,5,4)
    my $x = uniq Scalar, (1, 1, 2, 2, 3, 5, 3, 4);  # returns 5

    my @n = distinct "Mike", "Michael", "Richard", "Rick", "Michael", "Rick"
    # ("Mike", "Michael", "Richard", "Rick")

    my @s = distinct "A8", "", Str, "A5", "S1", "A5", "A8"
    # ("A8", "", Str, "A5", "S1")

    my @w = uniq "Giulia", "Giulietta", Str, "", 156, "Giulietta", "Giulia";
    # ("Giulia", "Giulietta", Str, "", 156)

C<distinct> is an alias for C<uniq>.

=head4 Idiomatic Raku ways

    my @x = (1, 1, 2, 2, 3, 5, 3, 4).unique;
    my $x = (1, 1, 2, 2, 3, 5, 3, 4).unique.elems;

=head3 singleton LIST

Returns a new list by stripping values in LIST occurring only once by
comparing the values as hash keys, except that type objects are considered
separate from ''.  The order of elements in the returned list is the same
as in LIST.  Returns the number of elements occurring only once in LIST
if the C<Scalar> positional parameter has been specified.

    my @x = singleton (1,1,4,2,2,3,3,5);          # returns (4,5)
    my $n = singleton Scalar, (1,1,4,2,2,3,3,5);  # returns 2

=head3 duplicates LIST

Returns a new list by stripping values in LIST occuring more than once by
comparing the values as hash keys, except that type objects are considered
separate from ''.  The order of elements in the returned list is the same
as in LIST.  Returns the number of elements occurring more than once in LIST.

    my @y = duplicates (1,1,2,4,7,2,3,4,6,9);          # returns (1,2,4)
    my $n = duplicates Scalar, (1,1,2,4,7,2,3,4,6,9);  # returns 3

=head4 Idiomatic Raku ways

    my @y = (1,1,2,4,7,2,3,4,6,9).repeated;
    my $n = (1,1,2,4,7,2,3,4,6,9).repeated.elems;

=head3 frequency LIST

Returns a hash of distinct values and the corresponding frequency.

    my %f := frequency values %radio_nrw; # returns (
    #  'Deutschlandfunk (DLF)' => 9, 'WDR 3' => 10,
    #  'WDR 4' => 11, 'WDR 5' => 14, 'WDR Eins Live' => 14,
    #  'Deutschlandradio Kultur' => 8,...)

=head4 Idiomatic Raku ways

    my %f := %radio_nrw.values.Bag;

=head3 occurrences LIST

Returns a new list of frequencies and the corresponding values from LIST.

    my @o = occurrences (1 xx 3, 2 xx 4, 3 xx 2, 4 xx 7, 5 xx 2, 6 xx 4);
    # (Any, Any, [3, 5], [1], [2, 6], Any, Any, [4])

=head3 mode LIST

Returns the modal value of LIST. Returns the modal value only if the
C<Scalar> positional parameter is specified.  Otherwise all probes occuring
I<modal> times are returned as well.

    my @m = mode (1 xx 3, 2 xx 4, 3 xx 2, 4 xx 7, 5 xx 2, 6 xx 7);
    #  (7, 4, 6)
    my $mode = mode Scalar, (1 xx 3, 2 xx 4, 3 xx 2, 4 xx 7, 5 xx 2, 6 xx 7);
    #  7

=head2 Partitioning

=head3 after BLOCK, LIST

Returns a list of the values of LIST after (and not including) the point
where BLOCK returns a true value. Passes the value as a parameter to
BLOCK for each element in LIST in turn.

    my @x = after { $_ %% 5 }, (1..9);   # returns (6, 7, 8, 9)

=head3 after_incl BLOCK, LIST

Same as C<after> but also includes the element for which BLOCK is true.

    my @x = after_incl { $_ %% 5 }, (1..9);   # returns (5, 6, 7, 8, 9)

=head4 Idiomatic Raku ways

    my @x = (1..9).toggle: * %% 5, :off;

=head3 before BLOCK, LIST

Returns a list of values of LIST up to (and not including) the point where
BLOCK returns a true value. Passes the value as a parameter to BLOCK for
each element in LIST in turn.

    my @x = before { $_ %% 5 }, (1..9);   # returns (1, 2, 3, 4)

=head4 Idiomatic Raku ways

    my @x = (1..9).toggle: * %% 5;

=head3 before_incl BLOCK LIST

Same as C<before> but also includes the element for which BLOCK is true.

    my @x = before_incl { $_ %% 5 }, (1..9);   # returns (1, 2, 3, 4, 5)

=head3 part BLOCK, LIST

Partitions LIST based on the return value of BLOCK which denotes into which
partition the current value is put.

Returns a list of the partitions thusly created. Each partition created is
an Array.

    my $i = 0;
    my @part = part { $i++ % 2 } (1..8); # returns ([1, 3, 5, 7], [2, 4, 6, 8])

You can have a sparse list of partitions as well where non-set partitions will
be an C<Array> type object:

    my @part = part { 2 } (1..5);        # returns (Array, Array, [1,2,3,4,5])

Be careful with negative values, though:

    my @part = part { -1 } (1..10);
    ===============================
    Unsupported use of a negative -1 subscript to index from the end

Negative values are only ok when they refer to a partition previously created:

    my @idx  = 0, 1, -1;
    my $i    = 0;
    my @part = part { $idx[$i++ % 3] }, (1..8); # ([1, 4, 7], [2, 3, 5, 6, 8])

=head3 samples COUNT, LIST

Returns a new list containing COUNT random samples from LIST. Is similar to
L<List::Util/shuffle>, but stops after COUNT.

    my @r  = samples 10, (1..10); # same as (1..10).pick(*)
    my @r2 = samples 5, (1..10);  # same as (1..10).pick(5)

=head4 Idiomatic Raku ways

    my @r  = (1..10).pick(*);
    my @r2 = (1..10).pick(5);

=head2 Iteration

=head3 each_array ARRAY1, ARRAY2 ...

Creates an array iterator to return the elements of the list of arrays ARRAY1,
ARRAY2 throughout ARRAYn in turn.  That is, the first time it is called, it
returns the first element of each array.  The next time, it returns the second
elements.  And so on, until all elements are exhausted.

This is useful for looping over more than one array at once:

    my &ea = each_array(@a, @b, @c);
    while ea() -> ($a,$b,$c) { .... }

The iterator returns the empty list when it reached the end of all arrays.

If the iterator is passed an argument of 'C<index>', then it returns
the index of the last fetched set of values, as a scalar.

=head4 Idiomatic Raku ways

    while zip(@a,@b,@c) -> ($a,$b,$c) { .... }

=head3 each_arrayref LIST

Like each_array, but the arguments is a single list with arrays.

=head3 natatime EXPR, LIST

Creates an array iterator, for looping over an array in chunks of
C<$n> items at a time.  (n at a time, get it?).  An example is
probably a better explanation than I could give in words.

Example:

    my @x = 'a'..'g';
    my &it = natatime 3, @x;
    while it() -> @vals {
        print "@vals[]\n";
    }

This prints

    a b c
    d e f
    g

=head4 Idiomatic Raku ways

    for @x.rotor(3,:partial) -> @vals {
        print "@vals[]\n";
    }

=head2 Searching

=head3 firstval BLOCK, LIST

=head3 first_value BLOCK, LIST

Returns the first element in LIST for which BLOCK evaluates to true. Each
element of LIST is passed to the BLOCK in turn. Returns C<Nil> if no such
element has been found.

    my @list = <alpha beta cicero bearing effortless>;
    say firstval { .starts-with('c') }, @list;  # cicero
    say firstval { .starts-with('b') }, @list;  # beta
    say firstval { .starts-with('g') }, @list;  # Nil, because never

C<first_value> is an alias for C<firstval>.

=head4 Idiomatic Raku ways

    say @list.first: *.starts-with('c');
    say @list.first: *.starts-with('b');
    say @list.first: *.starts-with('g');

=head3 onlyval BLOCK, LIST

=head3 only_value BLOCK, LIST

Returns the only element in LIST for which BLOCK evaluates to true. Each
element in LIST is passed to BLOCK in turn. Returns C<Nil> if no such element
has been found.

    my @list = <alpha beta cicero bearing effortless>;
    say onlyval { .starts-with('c') }, @list;  # cicero
    say onlyval { .starts-with('b') }, @list;  # Nil, because twice
    say onlyval { .starts-with('g') }, @list;  # Nil, because never

C<only_value> is an alias for C<onlyval>.

=head3 lastval BLOCK, LIST

=head3 last_value BLOCK, LIST

Returns the last value in LIST for which BLOCK evaluates to true. Each element
in LIST is passed to BLOCK in turn. Returns C<Nil> if no such element has been
found.

    my @list = <alpha beta cicero bearing effortless>;
    say lastval { .starts-with('c') }, @list;  # cicero
    say lastval { .starts-with('b') }, @list;  # bearing
    say lastval { .starts-with('g') }, @list;  # Nil, because never

C<last_value> is an alias for C<lastval>.

=head4 Idiomatic Raku ways

    say @list.first: *.starts-with('c'), :end;
    say @list.first: *.starts-with('b'), :end;
    say @list.first: *.starts-with('g'), :end;

=head3 firstres BLOCK, LIST

=head3 first_result BLOCK, LIST

Returns the result of BLOCK for the first element in LIST for which BLOCK
evaluates to true. Each element of LIST is passed to BLOCK in turn. Returns
C<Nil> if no such element has been found.

    my @list = <alpha beta cicero bearing effortless>;
    say firstres { .uc if .starts-with('c') }, @list;  # CICERO
    say firstres { .uc if .starts-with('b') }, @list;  # BETA
    say firstres { .uc if .starts-with('g') }, @list;  # Nil, because never

C<first_result> is an alias for C<firstres>.

=head3 onlyres BLOCK, LIST

=head3 only_result BLOCK, LIST

Returns the result of BLOCK for the first element in LIST for which BLOCK
evaluates to true. Each element of LIST is passed to BLOCK in turn. Returns
C<Nil> if no such element has been found.

    my @list = <alpha beta cicero bearing effortless>;
    say onlyres { .uc if .starts-with('c') }, @list;  # CICERO
    say onlyres { .uc if .starts-with('b') }, @list;  # Nil, because twice
    say onlyres { .uc if .starts-with('g') }, @list;  # Nil, because never

C<only_result> is an alias for C<onlyres>.

=head3 lastres BLOCK, LIST

=head3 last_result BLOCK, LIST

Returns the result of BLOCK for the last element in LIST for which BLOCK
evaluates to true. Each element of LIST is passed to BLOCK in turn. Returns
C<Nil> if no such element has been found.

    my @list = <alpha beta cicero bearing effortless>;
    say lastval { .uc if .starts-with('c') }, @list;  # CICERO
    say lastval { .uc if .starts-with('b') }, @list;  # BEARING
    say lastval { .uc if .starts-with('g') }, @list;  # Nil, because never

C<last_result> is an alias for C<lastres>.

=head3 indexes BLOCK, LIST

Evaluates BLOCK for each element in LIST (passed to BLOCK as the parameter)
and returns a list of the indices of those elements for which BLOCK returned
a true value. This is just like C<grep> only that it returns indices instead
of values:

    my @x = indexes { $_ %% 2 } (1..10);   # returns (1, 3, 5, 7, 9)

=head4 Idiomatic Raku ways

    my @x = (1..10).grep: * %% 2, :k;

=head3 firstidx BLOCK, LIST

=head3 first_index BLOCK, LIST

Returns the index of the first element in LIST for which the criterion in BLOCK
is true. Passes each element in LIST to BLOCK in turn:

    my @list = 1, 4, 3, 2, 4, 6;
    printf "item with index %i in list is 4", firstidx { $_ == 4 }, @list;
    ===============================
    item with index 1 in list is 4

Returns C<-1> if no such item could be found.

    my @list = 1, 3, 4, 3, 2, 4;
    print firstidx { $_ == 3 }, @list;    # 1
    print firstidx { $_ == 5 }, @list;    # -1, because not found

C<first_index> is an alias for C<firstidx>.

=head4 Idiomatic Raku ways

    printf "item with index %i in list is 4", @list.first: * == 4, :k;

    print @list.first: * == 3, :k;
    print @list.first( * == 5, :k) // -1;  # not found == Nil

=head3 onlyidx BLOCK, LIST

=head3 only_index BLOCK, LIST

Returns the index of the only element in LIST for which the criterion
in BLOCK is true. Passes each element in LIST to BLOCK in turn:

    my @list = 1, 3, 4, 3, 2, 4;
    printf "uniqe index of item 2 in list is %i", onlyidx { $_ == 2 }, @list;
    ===============================
    unique index of item 2 in list is 4

Returns C<-1> if either no such item or more than one of these has been found.

    my @list = 1, 3, 4, 3, 2, 4;
    print onlyidx { $_ == 3 }, @list;    # -1, because more than once
    print onlyidx { $_ == 5 }, @list;    # -1, because not found

C<only_index> is an alias for C<onlyidx>.

=head3 lastidx BLOCK, LIST

=head3 last_index BLOCK, LIST

Returns the index of the last element in LIST for which the criterion in BLOCK
is true. Passes each element in LIST to BLOCK in turn:

    my @list = 1, 4, 3, 2, 4, 6;
    printf "item with index %i in list is 4", lastidx { $_ == 4 } @list;
    ==================================
    item with index 4 in list is 4

Returns C<-1> if no such item could be found.

    my @list = 1, 3, 4, 3, 2, 4;
    print lastidx { $_ == 3 }, @list;    # 3
    print lastidx { $_ == 5 }, @list;    # -1, because not found

C<last_index> is an alias for C<lastidx>.

=head4 Idiomatic Raku ways

    printf "item with index %i in list is 4", @list.first: * == 4, :k, :end;

    print @list.first: * == 3, :k, :end;
    print @list.first( * == 5, :k, :end) // -1;  # not found == Nil

=head2 Sorting

=head3 sort_by BLOCK, LIST

Returns the list of values sorted according to the string values returned by
the BLOCK. A typical use of this may be to sort objects according to the
string value of some accessor, such as:

    my @sorted = sort_by { .name }, @people;

The key function is being passed each value in turn, The values are then
sorted according to string comparisons on the values returned.
This is equivalent to:

    my @sorted = sort -> $a, $b { $a.name cmp $b.name }, @people;

except that it guarantees the C<name> accessor will be executed only once
per value.  One interesting use-case is to sort strings which may have numbers
embedded in them "naturally", rather than lexically:

    my @sorted = sort_by { S:g/ (\d+) / { sprintf "%09d", $0 } / }, @strings;

This sorts strings by generating sort keys which zero-pad the embedded numbers
to some level (9 digits in this case), helping to ensure the lexical sort puts
them in the correct order.

=head4 Idiomatic Raku ways

    my @sorted = @people.sort: *.name;

=head3 nsort_by BLOCK, LIST

Similar to C<sort_by> but compares its key values numerically.

=head4 Idiomatic Raku ways

    my @sorted = <10 1 20 42>.sort: +*;

=head3 qsort BLOCK, ARRAY

This sorts the given array B<in place> using the given compare code.  The
Raku version uses the basic sort functionality as provided by the C<sort>
built-in function.

=head4 Idiomatic Raku ways

    @people .= sort;

=head2 Searching in sorted Lists

=head3 bsearch BLOCK, LIST

Performs a binary search on LIST which must be a sorted list of values.
BLOCK receives each element in turn and must return a negative value if the
element is smaller, a positive value if it is bigger and zero if it matches.

Returns a boolean value if the C<Scalar> named parameter is specified.
Otherwise it returns a single element list if it was found, or the empty list
if none of the calls to BLOCK returned C<0>.

    my @list  = <alpha beta cicero delta>;
    my @found = bsearch { $_ cmp "cicero" }, @list;   # ("cicero",)
    my @found = bsearch { $_ cmp "effort" }, @list;   # ()

    my @list  = <alpha beta cicero delta>;
    my $found = bsearch Scalar, { $_ cmp "cicero" }, @list;   # True
    my $found = bsearch Scalar, { $_ cmp "effort" }, @list;   # False

=head3 bsearchidx BLOCK, LIST

=head3 bsearch_index BLOCK, LIST

Performs a binary search on LIST which must be a sorted list of values.
BLOCK receives each element in turn and must return a negative value if the
element is smaller, a positive value if it is bigger and zero if it matches.

Returns the index of found element, otherwise C<-1>.

    my @list  = <alpha beta cicero delta>;
    my $found = bsearchidx { $_ cmp "cicero" }, @list;   # 2
    my $found = bsearchidx { $_ cmp "effort" }, @list;   # -1

C<bsearch_index> is an alias for C<bsearchidx>.

=head3 lower_bound BLOCK, LIST

Returns the index of the first element in LIST which does not compare
I<less than val>. Technically it's the first element in LIST which does
not return a value below zero when passed to BLOCK.

    my @ids = 1, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6;
    my $lb = lower_bound { $_ <=> 2 }, @ids; # 1
    my $lb = lower_bound { $_ <=> 4 }, @ids; # 9

=head3 upper_bound BLOCK, LIST

Returns the index of the first element in LIST which does not compare
I<greater than val>. Technically it's the first element in LIST which does
not return a value below or equal to zero when passed to BLOCK.

    my @ids = 1, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6;
    my $ub = upper_bound { $_ <=> 2 }, @ids; # 3
    my $ub = upper_bound { $_ <=> 4 }, @ids; # 13

=head3 equal_range BLOCK, LIST

Returns a list of indices containing the C<lower_bound> and the C<upper_bound>
of given BLOCK and LIST.

    my @ids = 1, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 6;
    my $er = equal_range { $_ <=> 2 }, @ids; # (1,3)
    my $er = equal_range { $_ <=> 4 }, @ids; # (9,13)

=head2 Operations on sorted Lists

=head3 binsert BLOCK, ITEM, LIST

=head3 bsearch_insert BLOCK, ITEM, LIST

Performs a binary search on LIST which must be a sorted list of values.
BLOCK must return a negative value if the current element (passed as a
parameter to the BLOCK) is smaller, a positive value if it is bigger and
zero if it matches.

ITEM is inserted at the index where the ITEM should be placed (based on above
search). That means, it's inserted before the next bigger element.

    my @l = 2,3,5,7;
    binsert { $_ <=> 4 },  4, @l; # @l = (2,3,4,5,7)
    binsert { $_ <=> 6 }, 42, @l; # @l = (2,3,4,5,42,7)

You take care that the inserted element matches the compare result.

C<bsearch_insert> is an alias for C<binsert>.

=head3 bremove BLOCK, LIST

=head3 bsearch_remove BLOCK, LIST

Performs a binary search on LIST which must be a sorted list of values.
BLOCK must return a negative value if the current element (passed as a
parameter to the BLOCK) is smaller, a positive value if it is bigger and
zero if it matches.

The item at the found position is removed and returned.

    my @l = 2,3,4,5,7;
    bremove { $_ <=> 4 }, @l; # @l = (2,3,5,7);

C<bsearch_remove> is an alias for C<bremove>.

=head2 Counting and calculation

=head3 true BLOCK, LIST

Counts the number of elements in LIST for which the criterion in BLOCK is true.
Passes each item in LIST to BLOCK in turn:

    printf "%i item(s) are defined", true { defined($_) }, @list;

=head4 Idiomatic Raku ways

    print "@list.grep(*.defined).elems() item(s) are defined";

=head3 false BLOCK, LIST

Counts the number of elements in LIST for which the criterion in BLOCK is false.
Passes each item in LIST to BLOCK in turn:

    printf "%i item(s) are not defined", false { defined($_) }, @list;

=head4 Idiomatic Raku ways

    print "@list.grep(!*.defined).elems() item(s) are not defined";

=head3 reduce_0 BLOCK, LIST

Reduce LIST by calling BLOCK in scalar context for each element of LIST.
The first parameter contains the progressional result and is initialized
with B<0>.  The second parameter contains the currently being processed
element of LIST.

    my $reduced = reduce_0 -> $a, $b { $a + $b }, @list;

In the Perl version, C<$_> is also set to the index of the element being
processed.  This is not the case in the Raku version for various reasons.
Should you need the index value in your calculation, you can post-increment
the anonymous state variable instead: C<$++>:

    my $reduced = reduce_0 -> $a, $b { dd $++ }, @list; # 0 1 2 3 4 5 ...

The idea behind reduce_0 is B<summation> (addition of a sequence of numbers).

=head3 reduce_1 BLOCK, LIST

Reduce LIST by calling BLOCK in scalar context for each element of LIST.
The first parameter contains the progressional result and is initialized
with B<1>.  The second parameter contains the currently being processed
element of LIST.

    my $reduced = reduce_1 -> $a, $b { $a * $b }, @list;

In the Perl version, C<$_> is also set to the index of the element being
processed.  This is not the case in the Raku version for various reasons.
Should you need the index value in your calculation, you can post-increment
the anonymous state variable instead: C<$++>:

    my $reduced = reduce_1 -> $a, $b { dd $++ }, @list; # 0 1 2 3 4 5 ...

The idea behind reduce_1 is B<product> of a sequence of numbers.

=head3 reduce_u BLOCK, LIST

Reduce LIST by calling BLOCK in scalar context for each element of LIST.
The first parameter contains the progressional result and is initialized
with B<Any>.  The second parameter contains the currently being processed
element of LIST.

    my $reduced = reduce_u -> $a, $b { $a.push($b) }, @list;

In the Perl version, C<$_> is also set to the index of the element being
processed.  This is not the case in the Raku version for various reasons.
Should you need the index value in your calculation, you can post-increment
the anonymous state variable instead: C<$++>:

    my $reduced = reduce_u -> $a, $b { dd $++ }, @list; # 0 1 2 3 4 5 ...

The idea behind reduce_u is to produce a list of numbers.

=head3 minmax LIST

Calculates the minimum and maximum of LIST and returns a two element list with
the first element being the minimum and the second the maximum. Returns the
empty list if LIST was empty.

    my ($min,$max) = minmax (43,66,77,23,780); # (23,780)

=head4 Idiomatic Raku ways

    my $range = <43,66,77,23,780>.minmax( +* );
    my $range = (43,66,77,23,780).minmax;   # auto-numerically compares

=head3 minmaxstr LIST

Computes the minimum and maximum of LIST using string compare and returns a
two element list with the first element being the minimum and the second the
maximum. Returns the empty list if LIST was empty.

    my ($min,$max) = minmaxstr <foo bar baz zippo>; # <bar zippo>

=head4 Idiomatic Raku ways

    my $range = (43,66,77,23,780).minmax( ~* );
    my $range = <foo bar baz zippo>.minmax;  # auto-string compares

=head1 SEE ALSO

L<List::Util>, L<List::AllUtils>, L<List::UtilsBy>

=head1 THANKS

Thanks to all of the individuals who have contributed to the Perl version
of this module.

=head1 AUTHOR

Elizabeth Mattijsen <liz@raku.rocks>

If you like this module, or what I’m doing more generally, committing to a
L<small sponsorship|https://github.com/sponsors/lizmat/>  would mean a great
deal to me!

Source can be located at: https://github.com/lizmat/List-MoreUtils . Comments
and Pull Requests are welcome.

=head1 COPYRIGHT AND LICENSE

Copyright 2018, 2019, 2020, 2021, 2023 Elizabeth Mattijsen

This library is free software; you can redistribute it and/or modify it under
the Artistic License 2.0.

Re-imagined from the Perl version as part of the CPAN Butterfly Plan. Perl
version originally developed by Tassilo von Parseval, subsequently maintained
by Adam Kennedy and Jens Rehsack.

=end pod

# vim: expandtab shiftwidth=4


