Collections
XFP comes with multiple collection types sharing an homogene API. It means different “how” but similar “what” following the functional paradigm of declarative programming.
from xfp import Xlist, Xiter
Xlist([1, 2, 3]).map(lambda x: x * 2)
Xiter([1, 2, 3]).map(lambda x: x * 2)
Despite those two lines declaring the same transformation, the one on Xlist is eager (it is immediately interpreted), while the one on Xiter is lazy (it is interpreted when needed on next()
calls).
Look at the specific documentation of each collection type for further information on implementation.
Common API
Transforming your data
When using the collection API, you can alter the collection using standard transformations.
map
provides a way to apply a transformation on each element of the collection.
filter
eliminates keep only elements of a collection that feat the given predicate.
flatten
explodes eventual other iterable existing inside the collection.
flat_map
combines map
and flatten
.
from xfp import Xlist
(
Xlist([1, 2, 3])
.map(lambda x: x * 10) # Xlist([10, 20, 30])
.filter(lambda x: x < 25) # Xlist([10, 20])
.flat_map(lambda x: [x, x]) # Xlist([10, 10, 20, 20])
)
Accessing your data
Once your transformations are described, you will want to exploit the result of the transformation. Every collection is homogene with the standard python collection API. You can indeed still use your favorite utils len
, iter
& co.
However the XFP wrapping provides a few other means.
get
is a synonym of __getitem__
.
head
is a synonym of get(0)
.
tail
returns a new collection without the first element (useful in recursion).
foreach
applies a procedure on every element without returning anything.
from xfp import Xlist
Xlist([1, 2, 3]).get(1) # returns 2
def concatenate(xlist: Xlist[int], multiple: int = 1, acc: int = 0) -> int:
if len(xlist) <= 0:
return acc
else:
return concatenate(xlist.tail(), multiple * 10, acc + xlist.head() * multiple)
concatenate(Xlist([1, 2, 3])) # returns 321
Xlist([1, 2, 3]).foreach(print) # display every element of the list
Note that get
, head
and tail
function also exists in a “_fr” version that returns a LEFT Xresult instead of raising an Error on empty list :
from xfp import Xlist
Xlist([]).head_fr() # Xresult(IndexError(...), XRBranch.LEFT)
Aggregating methods
Aggregation of lists is also facilitated using XFP.
fold
, fold_left
and fold_right
exist to aggregate a collection into an accumulator, providing its initial state.
reduce
is a shorthand for fold
with the initial accumulator state being set with the list’s first element (therefore it fails on empty list. Note that an “_fr” version also exists).
min
and max
are shorthand for reduce(lambda x, y: x if x < y else y)
(respectively >
).
from xfp import Xlist
Xlist([1, 2, 3]).fold(100)(lambda x, y: x + y) # returns 106
Xlist([100, 1, 2, 3]).reduce(lambda x, y: x + y) # returns 106
Copying your collections
Applying a transformation over a collection systematically creates a shallow copy of it.
copy
exists as a shorthand for map(lambda x: x)
. Use it to cleanly tee your collection, independently from the implementation. deepcopy
on the other hand will also recursively copy the content of the collections.
from xfp import Xlist
from dataclasses import dataclass
@dataclass
class Wrapper:
i: int
xlist = Xlist([Wrapper(1)])
shallow_xlist = xlist.copy()
deep_xlist = xlist.deepcopy()
shallow_xlist.get(0).i = 3
print(xlist.get(0)) # Wrapper(3)
print(deep_xlist.get(0)) # Wrapper(1)
Depending on the implementation, deepcopy
behavior can be tricky. In particular, you may want to read this about Xiter