Name

List::Stream - Simple Java-like lazy, functionally-pure manipulation of lists.

Synopsis

List::Stream provides simple functionality for manipulating list data in a functional, and simple way. List::Stream is lazy, meaning it stores all operations internally, so that they're only evaluated when entirely necessary.

Example

use List::Stream;
use DBI;

my $stream = stream DBI->selectall_array('SELECT * FROM users', {Slice => {}});

# create a sub stream that maps all users to their role
my $mapped_stream = $stream->map(sub { $_->{role} });
# to_list applies all pending lazy operations, ie map.
my $number_of_users  = $mapped_stream->filter(sub { $_ eq 'USER' })->count;
my $number_of_admins = $mapped_stream->filter(sub { $_ eq 'ADMIN' })->count;

my %users_by_user_id = $stream->to_hash(sub { $_->{user_id} }, sub { $_ });

my $it = $mapped_stream->to_iterator;

stream

Create a new List::Stream instance from a list.

use List::Stream;
my @data   = (1, 2, 3, 4);
my $stream = stream @data;

map

Map data in a stream over a unary function. A -> B

use List::Stream;
my @data   = (1, 2, 3, 4);
my $stream = stream @data;
@data = $stream
    ->map(sub { $_ + 1 })
    ->to_list;
say @data; # 2, 3, 4, 5

reduce

Reduce data to a single element, via a bi-function, with the default accumlator passed as the second arg. Retrieved by List::Stream::first. If the value reduced to is an ArrayRef, the streams data becomes the ArrayRef.

use List::Stream;
my @data   = (1, 2, 3, 4);
my $stream = stream @data;
my $sum = $stream
    ->reduce(sub {
        my ($elem, $accum) = @_;
        $accum += $elem;
    }, 0) # pass default
    ->first;
say $sum; # 10

filter

Filters elements from the stream if they do not pass a predicate.

use List::Stream;
my @data   = (1, 2, 3, 4);
my $stream = stream @data;
@data = $stream
    ->filter(sub { $_ >= 3 })
    ->to_list;
say @data; # 3, 4

flat_map

Passes the contents of the stream to a mapping function, the mapping function must then return a List::Stream.

use List::Stream;
my @data   = (1, 2, 3, 4);
my $stream = stream @data;
@data = $stream
    ->flat_map(sub {
        stream(@_)->map(sub { $_ * 2 })
    })
    ->to_list;
say @data; # 2, 4, 6, 8

unique

Filters the stream down to only unique values. This uses a HASH to determine uniqueness.

use List::Stream;
my $stream = stream qw(a a b c b d e);
my @values = $stream->unique->to_list;
say @values; # a, b, c, d, e

If you'd like to use another value to represent the value in the uniquness check you can pass a sub-routine that will be passed the value, and the result of the sub-routine will be the uniqueness identifier.

use List::Stream;
my $stream = stream ({ id => 123 }, { id => 456 }, { id => 123 });
my @values = $stream->unique(sub { $_->{id} })->to_list;
say @values; # { id => 123 }, { id => 456 }

skip

Skips n elements in the stream, discarding them.

use List::Stream;
my @data   = (1, 2, 3, 4);
my $stream = stream @data;
@data = $stream
    ->skip(2)
    ->to_list;
say @data; # 3, 4

for_each

Applies a void context unary-function to the stream.

use List::Stream;
my @data   = (1, 2, 3, 4);
my $stream = stream @data;
$stream->for_each(sub { say $_; }); # says 1, then 2, then 3, then 4

to_list

Applies all pending operations on the stream, and collects them to an array.

my @data   = (1, 2, 3, 4);
my $stream = stream(@data)->map(sub { $_ + 1 });
# The mapping hasn't happened yet, we're lazy.
@data = $stream->to_list;
say @data; # 2, 3, 4, 5

to_hash

Applies all pending operations on the stream, and collects them to a hash.

my @data   = (1, 2, 3, 4);
my $stream = stream(@data)->map(sub { $_ + 1 });
# The mapping hasn't happened yet, we're lazy.
my %hash = $stream->to_hash;
say %hash; # 2 => 3, 4 => 5

You may also provide a key, and value mapper to be applied to each element.

my @data = (1, 2, 3, 4);
my $stream = stream(@data)->map(sub { $_ + 1 });
my %hash = $stream->to_hash(sub { $_ * 2 }, sub { $_ });
say %hash; # 4 => 2, 6 => 3, 8 => 4, 10 => 5

first

Gets the first element of the stream, and applies all pending operations. This is useful when using List::Stream::reduce, when you've reduced to a single value.

use List::Stream;
my @data   = (1, 2, 3, 4);
my $stream = stream(@data)->map(sub { $_ + 1 });
my $first  = $stream->first;
say $first; # 1

Since reduce reduces the stream to a single element, first can be used to get the reduced value.

use List::Stream;
my @data   = (1, 2, 3, 4);
my $stream = stream(@data)
    ->map(sub { $_ + 1 })
    ->reduce(sub { my ($elem, $accum) = @_; $elem += $accum }, 0);
my $first  = $stream->first;
say $first; # 14

is_empty

Applies all pending operations, and returns true if the stream is empty or false if the stream has at least one value.

use List::Stream;
my $stream = stream(1,2,3,4);
say $stream->filter(sub { $_ > 5 })->is_empty; # true

to_iterator

Applies all pending operations on the stream, and returns an iterator in the form of a sub-routine.

use List::Stream;
my $stream = stream(qw(a b c d e f g))
    ->map(sub { $_ . 'f' });
my $it = $stream->to_iterator;
while (my $val = $it->()) {
    say $val;
}

count

Applies all pending operations on the stream, and returns the count of elements in the stream.

my $stream = stream(qw(a b c d e f g))
    ->map(sub { $_ . 'f' });
my $length = $stream->length;
say $length; # 7