Guide

Building method chains

In its most general form a FunctionalIterator takes an iterator and provides a functional and fluent interface on top of it:

>>> fs.FunctionalIterator([1, 2, 3, 4])
<fluentfs.common.functional.FunctionalIterator object at 0x7efd9fb46170>

The most important methods for building method chains are map, filter and reduce (that’s the functional part of the interface). Both map and filter again return a FunctionalIterator (that’s the fluent part of the interface). These methods are available on every FunctionalIterator:

>>> fs.FunctionalIterator([1, 2, 3, 4]).map(lambda f: f ** 2).list()
[1, 4, 9, 16]
>>> fs.FunctionalIterator([1, 2, 3, 4]).filter(lambda f: f % 2 == 0).list()
[2, 4]
>>> fs.FunctionalIterator([1, 2, 3, 4]).reduce(lambda x, y: x + y, 0)
10

Because the interface is fluent, you can build method chains on a FunctionalIterator:

>>> fs.FunctionalIterator([1, 2, 3, 4]).map(lambda f: f ** 2).filter(lambda f: f % 2 == 0).reduce(lambda x, y: x + y, 0)
20

Note that you can only iterate once through an iterator (this is not just the case for a FunctionalIterator but for all iterators). After that the iterator will be exhausted:

f = fs.FunctionalIterator([1, 2, 3, 4])
>>> f.list()
[1, 2, 3, 4]
>>> f.list()
[]

A minor technical point is that FunctionalIterator should not be built over any iterator, but only over a finite iterator. Otherwise most methods of FunctionalIterator will never return, trying to process an infinite iterator:

>>> from itertools import cycle
>>> f = fs.FunctionalIterator(cycle([1, 2, 3, 4]))
>>> f.list()
... this will never return ...

However you will rarely need to work with infinite iterators in the context of a filesystem.

Working with file iterators

The most important FunctionalIterator in fluentfs is the FileIterator. You could build one manually by creating an iterator of files and passing it to the FunctionalIterator constructor. However this is unnecessary, because usually you will get one by accessing the files property of a Dir object. Let’s assume that we have a directory /home/username/somedir which contains the text files a.txt, b.txt, c.py and the binary files bin1 and bin2:

>>> fs.Dir("/home/username/somedir").files
<fluentfs.filelike.file_likes.FileIterator object at 0x7f38d7f5cb80>
>>> fs.Dir("/home/username/somedir").files.list()
[File("/home/username/somedir/a.txt"), File("/home/username/somedir/b.txt"), File("/home/username/somedir/bin1"), File("/home/username/somedir/bin2"), File("/home/username/somedir/c.py")]

You can use map, filter and reduce on FunctionalIterator the same way you would use them on FileIterator. Note that in the context of a FunctionalIterator the map method returns a FunctionalIterator, but filter returns a FileIterator:

>>> fs.Dir("/home/username/somedir").files.map(lambda f: f.name)
<fluentfs.common.functional.FunctionalIterator object at 0x7f38d7f5cd00>
>>> fs.Dir("/home/username/somedir").files.map(lambda f: f.name).list()
['a.txt', 'b.txt', 'bin1', 'bin2', 'c.py']
>>> fs.Dir("/home/username/somedir").files.filter(lambda f: f.extension == "txt")
<fluentfs.filelike.file_likes.FileIterator object at 0x7f38d7eeffd0>
>>> fs.Dir("/home/username/somedir").files.filter(lambda f: f.extension == "txt").list()
[File("/home/username/somedir/a.txt"), File("/home/username/somedir/b.txt")]
>>> fs.Dir("/home/username/somedir").files.reduce(lambda bc, f: bc + f.bytes_count, 0)
24

The FileIterator provides you with various shortcuts for common map, filter and reduce operations. For example to map files by their byte counts you can use the map_byte_count:

>>> fs.Dir(".").files.map_byte_count().list()
[24, 46, 24, 32, 50]

To filter files by their extension you can use filter_extension:

>>> fs.Dir(".").files.filter_extension("txt").list()
[File("/home/username/somedir/a.txt"), File("/home/username/somedir/b.txt")]

Have a look at the documentation of FileIterator for more information.

Including and excluding files

A particularly important operation is to include or exclude (i.e. to generally filter) files based on some pattern recognition. The simplest one is to filter files by their extension, which we already discussed. A more complex one is to filter files by glob. For example to get all txt files that end with a.txt you would use the glob *a.txt:

fs.Dir(dir_path).files.filter_glob("*a.txt")

If you need to construct complex patterns, you can use regular expressions together with the filter_name_regex and filter_path_regex methods. These take a regular expression and keep only those file whose name or path respectively matches that regular expression. This is how you could keep all files whose name matches the regular expression a+.txt:

fs.Dir(dir_path).files.filter_name_regex(r"a+\.txt").list()