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