NAME
LINQ - the interface which all LINQ collections share
SYNOPSIS
use feature 'say';
use LINQ 'LINQ';
my $double_even_numbers =
LINQ( [1..100] )
->where( sub { $_ % 2 == 0 } )
->select( sub { $_ * 2 } );
if ( not $double_even_numbers->DOES( 'LINQ::Collection' ) ) {
die "What? But you said they all do it!";
}
for my $n ( $double_even_numbers->to_list ) {
say $n;
}
DESCRIPTION
Objects returned by the LINQ()
, LINQ::Repeat()
, and LINQ::Range()
functions all provide the LINQ::Collection interface. Many of the methods in this interface also return new objects which provide this interface.
METHODS
Many methods take a parameter "CALLABLE". This means they can accept a coderef, an object overloading &{}
, or an arrayref where the first item is one of the previous two things and the remainder are treated as arguments to curry to the first argument. A quoted regexp qr/.../
can also be used as a callable.
If using an arrayref, it is generally permissable to flatten it into a list, unless otherwise noted. An example of this can be seen in the documentation for select
.
select( CALLABLE )
-
LINQ's version of
map
, except that the code given is always called in scalar context, being expected to return exactly one result.Returns a LINQ::Collection of the results.
my $people = LINQ( [ { name => "Alice", age => 32 }, { name => "Bob", age => 31 }, { name => "Carol", age => 34 }, ] ); my $names = $people->select( sub { return $_->{name}; } ); for my $name ( $names->to_list ) { print "$name\n"; }
Another way of doing the same thing, using currying:
my $people = LINQ( [ { name => "Alice", age => 32 }, { name => "Bob", age => 31 }, { name => "Carol", age => 34 }, ] ); my $BY_HASH_KEY = sub { my ($key) = @_; return $_->{$key}; }; my $names = $people->select( $BY_HASH_KEY, 'name' ); for my $name ( $names->to_list ) { print "$name\n"; }
select_many( CALLABLE )
-
If you wanted
select
to be able to return a list likemap
does, thenselect_many
is what you want. However, rather than returning a Perl list, your callable should return a LINQ::Collection or an arrayref. where( CALLABLE )
-
LINQ's version of
grep
. Returns a LINQ::Collection of the filtered results.my $people = LINQ( [ { name => "Alice", age => 32 }, { name => "Bob", age => 31 }, { name => "Carol", age => 34 }, ] ); my $young_people = $people->where( sub { return $_->{age} < 33; } );
min( CALLABLE? )
-
Returns the numerically lowest value in the collection.
my $lowest = LINQ( [ 5, 1, 2, 3 ] )->min; # ==> 1
If a callable is provided, then
select
will be called and the minimum of the result will be returned.my $people = LINQ( [ { name => "Alice", age => 32 }, { name => "Bob", age => 31 }, { name => "Carol", age => 34 }, ] ); my $lowest_age = $people->min( sub { $_->{age} } ); # ==> 31
If you need more flexible comparison (e.g. non-numeric comparison), use
order_by
followed byfirst
. max( CALLABLE? )
-
Like
min
, but returns the numerically highest value. sum( CALLABLE? )
-
Like
min
, but returns the sum of all values in the collection. average( CALLABLE? )
-
Takes
sum
, and divides by the count of items in the collection.my $people = LINQ( [ { name => "Alice", age => 32 }, { name => "Bob", age => 31 }, { name => "Carol", age => 34 }, ] ); my $average_age = $people->average( sub { return $_->{age}; } ); # ==> 32.33333
aggregate( CALLABLE, INITIAL? )
-
LINQ's version of
reduce
(from List::Util). We pass$a
and$b
as the last arguments to CALLABLE, rather than using the package variables like List::Util does.The CALLABLE must not be a flattened list, but may still be an arrayref. INITIAL is an initial value.
my $people = LINQ( [ { name => "Alice", age => 32 }, { name => "Bob", age => 31 }, { name => "Carol", age => 34 }, ] ); my dotted_names = $people ->select( sub { $_->{name} } ) ->aggregate( sub { my ( $a, $b ) = @_; return "$a.$b"; } ); print "$dotted_names\n"; # ==> Alice.Bob.Carol
join( Y, HINT?, X_KEYS, Y_KEYS, JOINER )
-
This is akin to an SQL join.
my $people = LINQ( [ { name => "Alice", dept => 'Marketing' }, { name => "Bob", dept => 'IT' }, { name => "Carol", dept => 'IT' }, ] ); my $departments = LINQ( [ { dept_name => 'Accounts', cost_code => 1 }, { dept_name => 'IT', cost_code => 7 }, { dept_name => 'Marketing', cost_code => 8 }, ] ); my $BY_HASH_KEY = sub { my ($key) = @_; return $_->{$key}; }; my $joined = $people->join( $departments, -inner, # inner join [ $BY_HASH_KEY, 'dept' ], # select from $people [ $BY_HASH_KEY, 'dept_name' ], # select from $departments sub { my ( $person, $dept ) = @_; return { name => $person->{name}, dept => $person->{dept}, expense_code => $dept->{cost_code}, }; }, );
Hints
-inner
,-left
,-right
, and-outer
are supported, analagous to the joins with the same names in SQL.X_KEYS and Y_KEYS are non-list callables which return the values to join the two collections by.
JOINER is a callable (which may be a flattened list) which is passed items from each of the two collections and should return a new item. In the case of left/right/outer joins, one of those items may be undef.
group_join( Y, HINT?, X_KEYS, Y_KEYS, JOINER )
-
Similar to
group
except that rather than JOINER being called for every X/Y combination, all the Ys for a particular X are put in a collection, and the JOINER is called for each X and passed the collection of Ys.The only hints supported are
-inner
and-left
.This is best explained with a full example:
my $departments = LINQ( [ { dept_name => 'Accounts', cost_code => 1 }, { dept_name => 'IT', cost_code => 7 }, { dept_name => 'Marketing', cost_code => 8 }, ] ); my $people = LINQ( [ { name => "Alice", dept => 'Marketing' }, { name => "Bob", dept => 'IT' }, { name => "Carol", dept => 'IT' }, ] ); my $BY_HASH_KEY = sub { my ($key) = @_; return $_->{$key}; }; my $joined = $departments->group_join( $people, -left, # left join [ $BY_HASH_KEY, 'dept_name' ], # select from $departments [ $BY_HASH_KEY, 'dept' ], # select from $people sub { my ( $dept, $people ) = @_; # $people is a LINQ::Collection my $names = $people->select( $BY_HASH_KEY, 'name' )->to_array; return { dept => $dept->{dept_name}, cost_code => $dept->{cost_code}, people => $names, }; }, ); # [ # { # 'cost_code' => 1, # 'dept' => 'Accounts', # 'people' => [] # }, # { # 'cost_code' => 7, # 'dept' => 'IT', # 'people' => [ # 'Bob', # 'Carol' # ] # }, # { # 'cost_code' => 8, # 'dept' => 'Marketing', # 'people' => [ # 'Alice' # ] # }
take( N )
-
Takes just the first N items from a collection, returning a new collection.
take_while( CALLABLE )
-
Takes items from the collection, stopping at the first item where CALLABLE returns false.
If CALLABLE dies, there are some issues on older versions of Perl with the error message getting lost.
skip( N )
-
Skips the first N items from a collection, and returns the rest as a new collection.
skip_while( CALLABLE )
-
Skips the first items from a collection while CALLABLE returns true, and returns the rest as a new collection.
concat( COLLECTION )
-
Returns a new collection by concatenating this collection with another collection.
my $deck_of_cards = $red_cards->concat( $black_cards );
order_by( HINT?, CALLABLE? )
-
HINT may be
-numeric
(the default) or-string
.If CALLABLE is given, it should return a number or string to sort by.
my $sorted = $people->order_by( -string, [ $BY_HASH_KEY, 'name' ] # CALLABLE as an arrayref );
then_by( HINT?, CALLABLE )
-
Not implemented.
order_by_descending( HINT?, CALLABLE )
-
Like
order_by
but uses reverse order. then_by_descending( HINT?, CALLABLE )
-
Not implemented.
reverse
-
Reverses the order of the collection.
group_by( CALLABLE )
-
Groups the items by the key returned by CALLABLE.
The collection of groups is a LINQ::Collection and each grouping is a LINQ::Grouping. LINQ::Grouping provides two accessors:
key
andvalues
, withvalues
returning a LINQ::Collection of items.my $people = LINQ( [ { name => "Alice", dept => 'Marketing' }, { name => "Bob", dept => 'IT' }, { name => "Carol", dept => 'IT' }, ] ); my $groups = $people->group_by( sub { $_->{dept} } ); for my $group ( $groups->to_list ) { print "Dept: ", $group->key, "\n"; for my $person ( $group->values->to_list ) { print " - ", $person->{name}; } }
distinct( CALLABLE? )
-
Returns a new collection without any duplicates which were in the original.
If CALLABLE is provided, this will be used to determine when two items are considered identical/equivalent. Otherwise, numeric equality is used.
union( COLLECTION, CALLABLE? )
-
Returns a new collection formed from the union of both collections.
my $first = LINQ( [ 1, 2, 3, 4 ] ); my $second = LINQ( [ 3, 4, 5, 6 ] ); $first->union( $second )->to_array; # ==> [ 1, 2, 3, 4, 5, 6 ]
If CALLABLE is provided, this will be used to determine when two items are considered identical/equivalent. Otherwise, numeric equality is used.
intersect( COLLECTION, CALLABLE? )
-
Returns a new collection formed from the union of both collections.
my $first = LINQ( [ 1, 2, 3, 4 ] ); my $second = LINQ( [ 3, 4, 5, 6 ] ); $first->intersect( $second )->to_array; # ==> [ 3, 4 ]
If CALLABLE is provided, this will be used to determine when two items are considered identical/equivalent. Otherwise, numeric equality is used.
except( COLLECTION, CALLABLE? )
-
Returns a new collection formed from the asymmetric difference of both collections.
my $first = LINQ( [ 1, 2, 3, 4 ] ); my $second = LINQ( [ 3, 4, 5, 6 ] ); $first->except( $second )->to_array; # ==> [ 1, 2 ]
If CALLABLE is provided, this will be used to determine when two items are considered identical/equivalent. Otherwise, numeric equality is used.
sequence_equal( COLLECTION, CALLABLE? )
-
Returns true if and only if each item in the first collection is identical/equivalent to its corresponding item in the second collection, considered in order, according to CALLABLE.
If CALLABLE isn't given, then numeric equality is used.
first( CALLABLE? )
-
Returns the first item in a collection.
If CALLABLE is provided, returns the first item in the collection where CALLABLE returns true.
If there is no item to return, does not return undef, but throws a LINQ::Exception::NotFound exception.
first_or_default( CALLABLE?, DEFAULT )
-
Like
first
, but instead of throwing an exception, will return the DEFAULT. last( CALLABLE? )
-
Returns the last item in a collection.
If CALLABLE is provided, returns the last item in the collection where CALLABLE returns true.
If there is no item to return, does not return undef, but throws a LINQ::Exception::NotFound exception.
last_or_default( CALLABLE?, DEFAULT )
-
Like
last
, but instead of throwing an exception, will return the DEFAULT. single( CALLABLE? )
-
Returns the only item in a collection.
If CALLABLE is provided, returns the only item in the collection where CALLABLE returns true.
If there is no item to return, does not return undef, but throws a LINQ::Exception::NotFound exception.
If there are multiple items in the collection, or multiple items where CALLABLE returns true, throws a LINQ::Exception::MultipleFound exception.
single_or_default( CALLABLE?, DEFAULT )
-
Like
single
but rather than throwing an exception, will return DEFAULT. element_at( N )
-
Returns element N within the collection. N may be negative to count from the end of the collection. Collections are indexed from zero.
If N exceeds the length of the collection, throws a LINQ::Exception::NotFound exception.
element_at_or_default( N, DEFAULT )
-
Like
element_at
but rather than throwing an exception, will return DEFAULT. any( CALLABLE? )
-
Returns true if CALLABLE returns true for any item in the collection.
all( CALLABLE? )
-
Returns true if CALLABLE returns true for every item in the collection.
contains( ITEM, CALLABLE? )
-
Returns true if the collection contains ITEM. By default, this is checked using numeric equality.
If CALLABLE is given, this is passed two items and should return true if they should be considered identical/equivalent.
my $SAME_NAME = sub { $_[0]{name} eq $_[1]{name}; }; if ( $people->contains( { name => "Bob" }, $SAME_NAME ) ) { print "The collection includes Bob.\n"; }
count
-
Returns the size of the collection. (Number of items.)
to_list
-
Returns the collection as a list.
to_array
-
Returns an arrayref for the collection. This may be a tied arrayref and you should not assume it will be writable.
to_dictionary( CALLABLE )
-
The CALLABLE will be called for each item in the collection and is expected to return a string key.
The method will return a hashref mapping the keys to each item in the collection.
to_lookup( CALLABLE )
-
Alias for
to_dictionary
. to_iterator
-
Returns a coderef which can be used to iterate through the collection.
my $people = LINQ( [ { name => "Alice", dept => 'Marketing' }, { name => "Bob", dept => 'IT' }, { name => "Carol", dept => 'IT' }, ] ); my $next_person = $people->to_iterator; while ( my $person = $next_person->() ) { print $person->{name}, "\n"; }
cast( TYPE )
-
Given a type constraint (see Type::Tiny) will attempt to coerce every item in the collection to the type, and will return the collection of coerced values. If any item cannot be coerced, throws a LINQ::Exception::Cast exception.
of_type( TYPE )
-
Given a type constraint (see Type::Tiny) will attempt to coerce every item in the collection to the type, and will return the collection of coerced values. Any items which cannot be coerced will be skipped.
zip( COLLECTION, CALLABLE )
-
Will loop through both collections in parallel and pass one item from each collection to CALLABLE as arguments.
If the two collections are of different sizes, will stop after exhausing the shorter collection.
default_if_empty( ITEM )
-
If this collection contains one or more items, returns itself.
If the collection is empty, returns a new collection containing just a single item, given as a parameter.
my $collection = $people->default_if_empty( "Bob" ); # Equivalent to: my $collection = $people->count ? $people : LINQ( [ "Bob" ] );
foreach( CALLABLE )
-
This calls CALLABLE on each item in the collection. The following are roughly equivalent:
for ( $collection->to_list ) { say $_; } $collection->foreach( sub { say $_; } );
The advantage of the latter is that it avoids calling
to_list
, which would obviously fail on infinite collections.You can break out of the loop using
LINQ::LAST
.my $counter = 0; $collection->foreach( sub { say $_; LINQ::LAST if ++$counter >= 10; } );
Microsoft's official LINQ API doesn't include a
ForEach
method, but this method is provided by the MoreLINQ extension.
WORKING WITH INFINITE COLLECTIONS
Because LINQ collections can be instantiated from an iterator, they may contain infinite items.
Certain methods aggregate the entire collection, so can go into an infinite loop. This includes: aggregate
, min
, max
, sum
, average
, and count
.
Other methods which will go into an infinite loop on infinite collections: join
, group_join
, group_by
, order_by
, order_by_descending
, reverse
, to_lookup
, to_dictionary
, and to_list
.
The to_array
method in general will succeed on infinite collections as it can return a reference to a tied array. However, trying to dereference the entire array to a list will fail.
BUGS
Please report any bugs to http://rt.cpan.org/Dist/Display.html?Queue=LINQ.
SEE ALSO
https://en.wikipedia.org/wiki/Language_Integrated_Query
AUTHOR
Toby Inkster <tobyink@cpan.org>.
COPYRIGHT AND LICENCE
This software is copyright (c) 2014, 2021 by Toby Inkster.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
DISCLAIMER OF WARRANTIES
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.