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]) 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 >>> 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) >>> 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") >>> 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()