{"id":16907976,"url":"https://github.com/cjrh/excitertools","last_synced_at":"2025-03-22T10:31:23.212Z","repository":{"id":47465699,"uuid":"239741361","full_name":"cjrh/excitertools","owner":"cjrh","description":"itertools (and more-itertools) in the form of function call chaining (fluent interface)","archived":false,"fork":false,"pushed_at":"2025-01-27T14:06:22.000Z","size":270,"stargazers_count":14,"open_issues_count":15,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-13T20:38:37.382Z","etag":null,"topics":["chainable-methods","fluent-interface","itertools","itertools-library","pipeline-processor"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cjrh.png","metadata":{"files":{"readme":"README.rst","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["cjrh"]}},"created_at":"2020-02-11T11:04:07.000Z","updated_at":"2025-01-27T14:06:24.000Z","dependencies_parsed_at":"2025-01-25T17:32:03.864Z","dependency_job_id":null,"html_url":"https://github.com/cjrh/excitertools","commit_stats":null,"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjrh%2Fexcitertools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjrh%2Fexcitertools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjrh%2Fexcitertools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cjrh%2Fexcitertools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cjrh","download_url":"https://codeload.github.com/cjrh/excitertools/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244943777,"owners_count":20536290,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chainable-methods","fluent-interface","itertools","itertools-library","pipeline-processor"],"created_at":"2024-10-13T18:49:36.585Z","updated_at":"2025-03-22T10:31:23.191Z","avatar_url":"https://github.com/cjrh.png","language":"Python","funding_links":["https://github.com/sponsors/cjrh"],"categories":[],"sub_categories":[],"readme":"\n\n.. image:: https://github.com/cjrh/excitertools/workflows/Python%20application/badge.svg\n    :target: https://github.com/cjrh/excitertools/actions\n\n.. image:: https://coveralls.io/repos/github/cjrh/excitertools/badge.svg?branch=master\n    :target: https://coveralls.io/github/cjrh/excitertools?branch=master\n\n.. image:: https://img.shields.io/pypi/pyversions/excitertools.svg\n    :target: https://pypi.python.org/pypi/excitertools\n\n.. image:: https://img.shields.io/pypi/implementation/excitertools.svg\n    :target: https://pypi.python.org/pypi/excitertools\n\n.. image:: https://img.shields.io/github/tag/cjrh/excitertools.svg\n    :target: https://github.com/cjrh/excitertools\n\n.. image:: https://img.shields.io/badge/install-pip%20install%20excitertools-ff69b4.svg\n    :target: https://img.shields.io/badge/install-pip%20install%20excitertools-ff69b4.svg\n\n.. image:: https://img.shields.io/badge/dependencies-more--itertools-4488ff.svg\n    :target: https://more-itertools.readthedocs.io/en/stable/\n\n.. image:: https://img.shields.io/pypi/v/excitertools.svg\n    :target: https://img.shields.io/pypi/v/excitertools.svg\n\n.. image:: https://img.shields.io/badge/calver-YYYY.MM.MINOR-22bfda.svg\n    :target: http://calver.org/\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg\n    :target: https://github.com/ambv/black\n\n\n.. _more-itertools: https://more-itertools.readthedocs.io/en/stable/index.html\n\n.. _excitertools:\n\nexcitertools\n############\n\n``itertools`` reimagined as a `fluent interface \u003chttps://en.wikipedia.org/wiki/Fluent_interface\u003e`_.\n\n    In software engineering, a fluent interface is an object-oriented API whose design\n    relies extensively on method chaining. Its goal is to increase code legibility by\n    creating a domain-specific language (DSL). The term was coined in 2005 by Eric\n    Evans and Martin Fowler.\n\n    `*Wikipedia - Fluent Interface* \u003chttps://en.wikipedia.org/wiki/Fluent_interface\u003e`_\n\nNote that nearly all of the ``more-itertools`` extension library is included.\n\nCustom GPT\n**********\n\nI made a custom GPT `here at chatgpt.com \u003chttps://chatgpt.com/g/g-679767714b688191a719d7db72b72261-excitertools-helper\u003e`_\nto help with coding using excitertools as well as answering questions about it.\nThe GPT is set up with this complete README as its knowledge base. This is\nmore of an experiement than anything else, but I am curious to know whether it\nis useful.\n\nDemo\n****\n\n.. code-block:: python\n\n    \u003e\u003e\u003e range(10).map(lambda x: x*7).filter(lambda x: x % 3 == 0).collect()\n    [0, 21, 42, 63]\n    \u003e\u003e\u003e range(10).map(lambda x: x*7).filter(lambda x: x \u003e 0 and x % 3 == 0).collect()\n    [21, 42, 63]\n\nWhen the lines get long, parens can be used to split up each instruction:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e (\n    ...     range(10)\n    ...         .map(lambda x: x*7)\n    ...         .filter(lambda x: x % 3 == 0)\n    ...         .collect()\n    ... )\n    [0, 21, 42, 63]\n\nWhat's also interesting about that is how lambda's can easily contain these\nprocessing chains, since an entire chain is a single expression. For\nexample:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e names = ['caleb', 'james', 'gina']\n    \u003e\u003e\u003e Iter(names).map(\n    ...     lambda name: (\n    ...         Iter(name)\n    ...             .map(lambda c: c.upper() if c in 'aeiouy' else c)\n    ...             .collect(str)\n    ...     )\n    ... ).collect()\n    ['cAlEb', 'jAmEs', 'gInA']\n\nSomething I've noticed is that ``reduce`` seems easier to use and reason\nabout with this fluent interface, as compared to the conventional usage\nas a standalone function; also, the operator module makes ``reduce`` quite\nuseful for simple cases:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from operator import add, mul\n    \u003e\u003e\u003e (\n    ...     range(10)\n    ...     .map(lambda x: x*7)\n    ...     .filter(lambda x: x \u003e 0 and x % 3 == 0)\n    ...     .reduce(add)\n    ... )\n    126\n    \u003e\u003e\u003e (\n    ...     range(10)\n    ...     .map(lambda x: x*7)\n    ...     .filter(lambda x: x \u003e 0 and x % 3 == 0)\n    ...     .reduce(mul)\n    ... )\n    55566\n\n.. contents::\n    :depth: 1\n\n\n.. |warning| unicode:: U+26A0\n.. |cool| unicode:: U+2728\n.. |flux| unicode:: U+1F6E0\n.. |source| unicode:: U+1F3A4\n.. |sink| unicode:: U+1F3A7\n.. |inf| unicode:: U+267E\n\n\nHow to Understand the API Documentation\n#######################################\n\nSeveral symbols are used to indicate things about parts of the API:\n\n- |source| This function is a *source*, meaning that it produces data\n  that will be processed in an iterator chain.\n- |sink| This function is a *sink*, meaning that it consumes data that\n  was processed in an iterator chain.\n- |inf| This function returns an infinite iterable\n- |warning| Warning - pay attention\n- |flux| This API is still in flux, and might be changed or\n  removed in the future\n- |cool| Noteworthy; could be especially useful in many situations.\n\nThe API is arranged roughly with the module-level functions first, and\nthereafter the Iter_ class itself. It is the Iter_ class that does\nthe work to allow these iterators to be chained together. However, the\nmodule-level functions are more likely to be used directly and that's\nwhy they're presented first.\n\nThe API includes wrappers for the stdlib *itertools* module, including\nthe \"recipes\" given in the *itertools* docs, as well as wrappers for\nthe iterators from the more-itertools_ 3rd-party package.\n\nModule-level Replacements for Builtins\n######################################\n\nThe following module-level functions, like range_, zip_ and so on, are\nintended to be used as replacements for their homonymous builtins. The\nonly difference between these and the builtin versions is that these\nreturn instances of the Iter_ class. Note that because Iter_ is itself\niterable, it means that the functions here can be used as drop-in\nreplacements.\n\nOnce you have an Iter_ instance, all of its methods become available\nvia function call chaining, so these toplevel functions are really only\na convenience to \"get started\" using the chaining syntax with minimal\nupfront cost in your own code.\n\n.. contents::\n    :local:\n\n\n\n.. _range:\n\n\n|source| ``range(*args) -\u003e \"Iter[int]\"``\n****************************************\n\n\nReplacement for the builtin ``range`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nAll the same calling variations work because this function merely wraps\nthe original function.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e range(3).collect()\n    [0, 1, 2]\n    \u003e\u003e\u003e range(1, 4).collect()\n    [1, 2, 3]\n    \u003e\u003e\u003e range(1, 6, 2).collect()\n    [1, 3, 5]\n    \u003e\u003e\u003e range(1, 101, 3).filter(lambda x: x % 7 == 0).collect()\n    [7, 28, 49, 70, 91]\n\nThis example multiples, element by element, the series ``[0:5]`` with the\nseries ``[1:6]``. Two things to note: Firstly, Iter.zip_ is used to emit\nthe tuples from each series. Secondly, Iter.starmap_ is used to receive\nthose tuples into separate arguments in the ``lambda``.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e range(5).zip(range(1, 6)).starmap(lambda x, y: x * y).collect()\n    [0, 2, 6, 12, 20]\n\nWhen written in a single line as above, it can get difficult to follow\nthe chain of logic if there are many processing steps. Parentheses in\nPython allow grouping such that expressions can be spread over multiple\nlines.\n\nThis is the same example as the prior one, but formatted to be spread\nover several lines. This is much clearer:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e # Written out differently\n    \u003e\u003e\u003e (\n    ...     range(5)\n    ...         .zip(range(1, 6))\n    ...         .starmap(lambda x, y: x * y)\n    ...         .collect()\n    ... )\n    [0, 2, 6, 12, 20]\n\nIf you wanted the sum instead, it isn't necessary to do the collection\nat all:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e (\n    ...     range(5)\n    ...         .zip(range(1, 6))\n    ...         .starmap(lambda x, y: x * y)\n    ...         .sum()\n    ... )\n    40\n\n\n\n.. _zip:\n\n\n``zip(*iterables: Any) -\u003e \"Iter[Tuple[T, ...]]\"``\n*************************************************\nReplacement for the builtin ``zip`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. _enumerate:\n\n\n``enumerate(iterable) -\u003e \"Iter[Tuple[int, T]]\"``\n************************************************\nReplacement for the builtin ``enumerate`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e import string\n    \u003e\u003e\u003e enumerate(string.ascii_lowercase).take(3).collect()\n    [(0, 'a'), (1, 'b'), (2, 'c')]\n\n\n\n\n.. _map:\n\n\n``map(func: Union[Callable[..., C], str], iterable) -\u003e \"Iter[C]\"``\n******************************************************************\nReplacement for the builtin ``map`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e result = map(lambda x: (x, ord(x)), 'caleb').dict()\n    \u003e\u003e\u003e assert result == {'a': 97, 'b': 98, 'c': 99, 'e': 101, 'l': 108}\n\n    \u003e\u003e\u003e result = map('x, ord(x)', 'caleb').dict()\n    \u003e\u003e\u003e assert result == {'a': 97, 'b': 98, 'c': 99, 'e': 101, 'l': 108}\n\n\n.. _filter:\n\n\n``filter(function: \"Callable[[Any], bool]\", iterable: \"Iterable[T]\") -\u003e \"Iter[T]\"``\n***********************************************************************************\nReplacement for the builtin ``filter`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e filter(lambda x: x % 3 == 0, range(10)).collect()\n    [0, 3, 6, 9]\n\n\n\n\n.. _count:\n\n\n|source| ``count(start=0, step: int = 1) -\u003e \"Iter[int]\"``\n*********************************************************\n\n\nReplacement for the itertools ``count`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e count().take(5).collect()\n    [0, 1, 2, 3, 4]\n    \u003e\u003e\u003e count(0).take(0).collect()\n    []\n    \u003e\u003e\u003e count(10).take(0).collect()\n    []\n    \u003e\u003e\u003e count(10).take(5).collect()\n    [10, 11, 12, 13, 14]\n    \u003e\u003e\u003e count(1).filter(lambda x: x \u003e 10).take(5).collect()\n    [11, 12, 13, 14, 15]\n\n\n\n.. _cycle:\n\n\n``cycle(iterable) -\u003e \"Iter[T]\"``\n********************************\nReplacement for the itertools ``count`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e cycle(range(3)).take(6).collect()\n    [0, 1, 2, 0, 1, 2]\n    \u003e\u003e\u003e cycle([]).take(6).collect()\n    []\n    \u003e\u003e\u003e cycle(range(3)).take(0).collect()\n    []\n\n\n\n.. _repeat:\n\n\n|source| ``repeat(object: C, times=None) -\u003e \"Iter[C]\"``\n*******************************************************\n\n\nReplacement for the itertools ``count`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e repeat('a').take(3).collect()\n    ['a', 'a', 'a']\n    \u003e\u003e\u003e repeat([1, 2]).take(3).collect()\n    [[1, 2], [1, 2], [1, 2]]\n    \u003e\u003e\u003e repeat([1, 2]).take(3).collapse().collect()\n    [1, 2, 1, 2, 1, 2]\n    \u003e\u003e\u003e repeat([1, 2]).collapse().take(3).collect()\n    [1, 2, 1]\n    \u003e\u003e\u003e repeat('a', times=3).collect()\n    ['a', 'a', 'a']\n\n\n\n\nThis next set of functions return iterators that terminate on the shortest \ninput sequence.\n\n\n\n.. _accumulate:\n\n\n``accumulate(iterable, func=None, *, initial=None)``\n****************************************************\nReplacement for the itertools ``accumulate`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e accumulate([1, 2, 3, 4, 5]).collect()\n    [1, 3, 6, 10, 15]\n    \u003e\u003e\u003e if sys.version_info \u003e= (3, 8):\n    ...     output = accumulate([1, 2, 3, 4, 5], initial=100).collect()\n    ...     assert output == [100, 101, 103, 106, 110, 115]\n    \u003e\u003e\u003e accumulate([1, 2, 3, 4, 5], operator.mul).collect()\n    [1, 2, 6, 24, 120]\n    \u003e\u003e\u003e accumulate([]).collect()\n    []\n    \u003e\u003e\u003e accumulate('abc').collect()\n    ['a', 'ab', 'abc']\n    \u003e\u003e\u003e accumulate(b'abc').collect()\n    [97, 195, 294]\n    \u003e\u003e\u003e accumulate(bytearray(b'abc')).collect()\n    [97, 195, 294]\n\n\n\n.. _chain:\n\n\n``chain(*iterables: Iterable[T]) -\u003e \"Iter[T]\"``\n***********************************************\nReplacement for the itertools ``chain`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e chain('ABC', 'DEF').collect()\n    ['A', 'B', 'C', 'D', 'E', 'F']\n    \u003e\u003e\u003e chain().collect()\n    []\n\n\n\n.. _chain_from_iterable:\n\n\n``chain_from_iterable(iterable) -\u003e \"Iter[T]\"``\n**********************************************\nReplacement for the itertools ``chain.from_iterable`` method.\nThis version returns an instance of Iter_ to allow\nfurther iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e chain_from_iterable(['ABC', 'DEF']).collect()\n    ['A', 'B', 'C', 'D', 'E', 'F']\n    \u003e\u003e\u003e chain_from_iterable([]).collect()\n    []\n\n\n\n.. _compress:\n\n\n``compress(data, selectors)``\n*****************************\nReplacement for the itertools ``compress`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e compress('ABCDEF', [1, 0, 1, 0, 1, 1]).collect()\n    ['A', 'C', 'E', 'F']\n\n\n\n\n.. _dropwhile:\n\n\n``dropwhile(pred, iterable)``\n*****************************\nReplacement for the itertools ``dropwhile`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e dropwhile(lambda x: x \u003c 4, range(6)).collect()\n    [4, 5]\n\n\n\n.. _filterfalse:\n\n\n``filterfalse(pred, iterable)``\n*******************************\nReplacement for the itertools ``filterfalse`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e filterfalse(None, [2, 0, 3, None, 4, 0]).collect()\n    [0, None, 0]\n\n\n\n.. _groupby:\n\n\n``groupby(iterable, key=None)``\n*******************************\nReplacement for the itertools ``groupby`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\ngroupby_ returns an iterator of a key and \"grouper\" iterable. In the\nexample below, we use Iter.starmap_ to collect each grouper iterable\ninto a list, as this makes it neater for display here in the docstring.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e (\n    ...     groupby(['john', 'jill', 'anne', 'jack'], key=lambda x: x[0])\n    ...         .starmap(lambda k, g: (k, list(g)))\n    ...         .collect()\n    ... )\n    [('j', ['john', 'jill']), ('a', ['anne']), ('j', ['jack'])]\n\n\n\n\n.. _islice:\n\n\n``islice(iterable, *args) -\u003e \"Iter\"``\n*************************************\nReplacement for the itertools ``islice`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e islice('ABCDEFG', 2).collect()\n    ['A', 'B']\n    \u003e\u003e\u003e islice('ABCDEFG', 2, 4).collect()\n    ['C', 'D']\n    \u003e\u003e\u003e islice('ABCDEFG', 2, None).collect()\n    ['C', 'D', 'E', 'F', 'G']\n    \u003e\u003e\u003e islice('ABCDEFG', 0, None, 2).collect()\n    ['A', 'C', 'E', 'G']\n\n\n\n.. _starmap:\n\n\n``starmap(func, iterable)``\n***************************\nReplacement for the itertools ``starmap`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e starmap(pow, [(2, 5), (3, 2), (10, 3)]).collect()\n    [32, 9, 1000]\n\n\n\n.. _takewhile:\n\n\n``takewhile(pred, iterable)``\n*****************************\nReplacement for the itertools ``takewhile`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e takewhile(lambda x: x \u003c 5, [1, 4, 6, 4, 1]).collect()\n    [1, 4]\n\n\n\n.. _tee:\n\n\n``tee(iterable, n=2)``\n**********************\nReplacement for the itertools ``tee`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e a, b = tee(range(5))\n    \u003e\u003e\u003e a.collect()\n    [0, 1, 2, 3, 4]\n    \u003e\u003e\u003e b.sum()\n    10\n\nIt is also possible to operate on the returned iterators in the chain\nbut it gets quite difficult to understand:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e tee(range(5)).map(lambda it: it.sum()).collect()\n    [10, 10]\n\nIn the example above we passed in range_, but with excitertools_\nit's usually more natural to push data sources further left:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e range(5).tee().map(lambda it: it.sum()).collect()\n    [10, 10]\n\nPay close attention to the above. The map_ is acting on each of the\ncopied iterators.\n\n\n\n.. _zip_longest:\n\n\n``zip_longest(*iterables, fillvalue=None)``\n*******************************************\nReplacement for the itertools ``zip_longest`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e zip_longest('ABCD', 'xy', fillvalue='-').collect()\n    [('A', 'x'), ('B', 'y'), ('C', '-'), ('D', '-')]\n    \u003e\u003e\u003e (\n    ...     zip_longest('ABCD', 'xy', fillvalue='-')\n    ...         .map(lambda tup: concat(tup, ''))\n    ...         .collect()\n    ... )\n    ['Ax', 'By', 'C-', 'D-']\n    \u003e\u003e\u003e (\n    ...     zip_longest('ABCD', 'xy', fillvalue='-')\n    ...         .starmap(operator.add)\n    ...         .collect()\n    ... )\n    ['Ax', 'By', 'C-', 'D-']\n\n\n\n.. _finditer_regex:\n\n\n``finditer_regex(pat: \"re.Pattern[AnyStr]\", s: AnyStr, flags: Union[int, re.RegexFlag] = 0) -\u003e \"Iter[AnyStr]\"``\n***************************************************************************************************************\n\nWrapper for ``re.finditer``. Returns an instance of Iter_ to allow\nchaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e pat = r\"\\w+\"\n    \u003e\u003e\u003e text = \"Well hello there! How ya doin!\"\n    \u003e\u003e\u003e finditer_regex(pat, text).map(str.lower).filter(lambda w: 'o' in w).collect()\n    ['hello', 'how', 'doin']\n    \u003e\u003e\u003e finditer_regex(r\"[A-Za-z']+\", \"A programmer's RegEx test.\").collect()\n    ['A', \"programmer's\", 'RegEx', 'test']\n    \u003e\u003e\u003e finditer_regex(r\"[A-Za-z']+\", \"\").collect()\n    []\n    \u003e\u003e\u003e finditer_regex(\"\", \"\").collect()\n    ['']\n    \u003e\u003e\u003e finditer_regex(\"\", \"\").filter(None).collect()\n    []\n\n\n\n.. _splititer_regex:\n\n\n``splititer_regex(pat: \"re.Pattern[AnyStr]\", s: AnyStr, flags: Union[int, re.RegexFlag] = 0) -\u003e \"Iter[AnyStr]\"``\n****************************************************************************************************************\n\nLazy string splitting using regular expressions.\n\nMost of the time you want ``str.split``. Really! That will almost\nalways be fastest. You might think that ``str.split`` is inefficient\nbecause it always has to build a list, but it can do this very, very\nquickly.\n\nThe lazy splitting shown here is more about supporting a particular\nkind of programming model, rather than performance.\n\nSee more discussion `here \u003chttps://stackoverflow.com/questions/3862010/is-there-a-generator-version-of-string-split-in-python\u003e`_.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e splititer_regex(r\"\\s\", \"A programmer's RegEx test.\").collect()\n    ['A', \"programmer's\", 'RegEx', 'test.']\n\nNote that splitting at a single whitespace character will return blanks\nfor each found. This is different to how ``str.split()`` works.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e splititer_regex(r\"\\s\", \"aaa     bbb  \\n  ccc\\nddd\\teee\").collect()\n    ['aaa', '', '', '', '', 'bbb', '', '', '', '', 'ccc', 'ddd', 'eee']\n\nTo match ``str.split()``, specify a sequence of whitespace as the\nregex pattern.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e splititer_regex(r\"\\s+\", \"aaa     bbb  \\n  ccc\\nddd\\teee\").collect()\n    ['aaa', 'bbb', 'ccc', 'ddd', 'eee']\n\nCounting the whitespace\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from collections import Counter\n    \u003e\u003e\u003e splititer_regex(r\"\\s\", \"aaa     bbb  \\n  ccc\\nddd\\teee\").collect(Counter)\n    Counter({'': 8, 'aaa': 1, 'bbb': 1, 'ccc': 1, 'ddd': 1, 'eee': 1})\n\nLazy splitting at newlines\n\n.. code-block:: python\n\n    \u003e\u003e\u003e splititer_regex(r\"\\n\", \"aaa     bbb  \\n  ccc\\nddd\\teee\").collect()\n    ['aaa     bbb  ', '  ccc', 'ddd\\teee']\n\nA few more examples:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e splititer_regex(r\"\", \"aaa\").collect()\n    ['', 'a', 'a', 'a', '']\n    \u003e\u003e\u003e splititer_regex(r\"\", \"\").collect()\n    ['', '']\n    \u003e\u003e\u003e splititer_regex(r\"\\s\", \"\").collect()\n    ['']\n    \u003e\u003e\u003e splititer_regex(r\"a\", \"\").collect()\n    ['']\n    \u003e\u003e\u003e splititer_regex(r\"\\s\", \"aaa\").collect()\n    ['aaa']\n\n\n\n.. _concat:\n\n\n``concat(iterable: Iterable[AnyStr], glue: AnyStr) -\u003e \"AnyStr\"``\n****************************************************************\nConcatenate strings, bytes and bytearrays. It is careful to avoid the\nproblem with single bytes becoming integers, and it looks at the value\nof `glue` to know whether to handle bytes or strings.\n\nThis function can raise ``ValueError`` if called with something\nother than ``bytes``, ``bytearray`` or ``str``.\n\n.. _from_queue:\n\n\n|cool| |source| ``from_queue(q: queue.Queue, timeout=None, sentinel=None) -\u003e \"Iter\"``\n*************************************************************************************\n\n\n\n\nWrap a queue with an iterator interface. This allows it to participate\nin chaining operations. The iterator will block while waiting for\nnew values to appear on the queue. This is useful: it allows you\nto easily and safely pass data between threads or processes, and\nfeed the incoming data into a pipeline.\n\nThe sentinel value, default ``None``, will terminate the iterator.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e q = queue.Queue()\n    \u003e\u003e\u003e # This line puts stuff onto a queue\n    \u003e\u003e\u003e range(10).chain([None]).map(q.put).consume()\n    \u003e\u003e\u003e from_queue(q).filter(lambda x: 2 \u003c x \u003c 9).collect()\n    [3, 4, 5, 6, 7, 8]\n\nThis can be used in the same way you would normally use a queue, in\nthat it will block while waiting for future input. This makes it\nconvenient to run in a thread and wait for work. Below is a rough\nsketch of how one might cobble together a thread pool using this\nfeature. Note the use of Iter.side_effect_ to call ``task_done()``\non the queue.\n\n.. code-block:: python\n\n    import queue\n    from threading import Thread\n    import logging\n    from excitertools import from_queue\n\n    logger = logging.getLogger(__name__)\n\n    def process_job(job):\n        result = ...\n        return result\n\n    def worker(inputs: Queue, results: Queue):\n        (\n            from_queue(inputs)\n            .side_effect(lambda job: logger.info(f\"Received job {job}\")\n            .map(process_job)\n            .side_effect(lambda result: logger.info(f\"Got result {job}\")\n            .into_queue(results)\n            # Note that we only mark the task as done after the result\n            # is added to the results queue.\n            .side_effect(lambda _: inputs.task_done()\n        )\n\n    def create_job_pool(n: int) -\u003e Tuple[Queue, Queue, Callable]:\n        \"\"\"Returns two queues, and a pool shutdown method. The\n        shutdown function can be called to shut down the pool and\n        the ``inputs`` queue. Caller is responsible for draining\n        the ``results`` queue.\"\"\"\n\n        # Independent control of the sizes of the input and output\n        # queues is interesting: it lets you decide how to bias\n        # backpressure depending on the details of your workload.\n        inputs, results = Queue(maxsize=100), Queue(maxsize=3)\n\n        kwargs = dict(target=worker, args=(inputs, results), daemon=True)\n        threads = repeat(Thread).map(lambda T: T(**kwargs)).take(n).collect()\n\n        def shutdown():\n            # Shut down each thread\n            repeat(None).map(inputs.put).take(n).consume()\n            inputs.join()\n            Iter(threads).map(lambda t: t.join()).consume()\n\n        return inputs, results, shutdown\n\nNow the two queues ``inputs`` and ``results`` can be used in various\nother threads to supply and consume data.\n\n\n\n.. _fileinput:\n\n\n|source| ``fileinput(files=None, inplace=False, backup=\"\", mode=\"r\", openhook=None, encoding=None, errors=None) -\u003e Iter``\n*************************************************************************************************************************\n\n\nA wrapper around fileinput.input that returns an Excitertools Iter_ instance.\n\nThe documentation for the stdlib fileinput module is here:\n    https://docs.python.org/3/library/fileinput.html\n\nHere is an example of use:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from excitertools import fileinput\n    \u003e\u003e\u003e # Read from a file\n    \u003e\u003e\u003e fileinput(['data.txt']).take(3).collect()                  \n    ['1', '2', '3']\n    \u003e\u003e\u003e # Read from stdin OR files listed on the command line (sys.argv[1:])\n    \u003e\u003e\u003e fileinput().take(3).collect()                  \n    ['1', '2', '3']\n\n:param files: A list of filenames or '-' for stdin (default: sys.argv[1:]).\n:param inplace: Whether to allow in-place editing (default: False).\n:param backup: Backup extension for in-place editing (default: \"\").\n:param mode: File mode, e.g., 'r' or 'rb' (default: 'r').\n:param openhook: Optional hook to customize file opening.\n:param encoding: File encoding (default: None).\n:param errors: Error handling mode (default: None).\n\n\n\nThe ``Iter`` Class\n##################\n\n.. contents::\n    :backlinks: entry\n    :local:\n\n\n\n.. _Iter:\n\n\n|cool| ``class Iter(Generic[T], Iterator[T])``\n**********************************************\n\n\nThis class is what allows chaining. Many of the methods in this class\nreturn an instance of Iter_, which allows further chaining. There\nare two exceptions to this: *sources* and *sinks*.\n\nA \"source\" is usually a ``classmethod`` which can be used as an\ninitializer to produce data via an iterable. For example, the Iter.range_\nclassmethod can be used to get a sequence of numbers:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.range(1_000_000).take(3).collect()\n    [0, 1, 2]\n\nEven though our range was a million elements, the iterator chaining\ntook only 3 of those elements before collecting.\n\nA \"sink\" is a method that is usually the last component of a processing\nchain and often (but not always!) consumes the entire iterator. In the\nexample above, the call to Iter.collect_ was a sink. Note that we still\ncall it a sink even though it did not consume the entire iterator.\n\nWe're using the term \"source\" to refer to a classmethod of Iter_ that\nproduces data; but, the most typical source is going to be data that\nyou provide. Iter_ can be called with anything that is iterable, including\nsequences, iterators, mappings, sets, generators and so on.\n\nExamples:\n\n.. code-block:: python\n\n    List\n    \u003e\u003e\u003e Iter([1, 2, 3]).map(lambda x: x * 2).sum()\n    12\n\n    Generator\n    \u003e\u003e\u003e Iter((1, 2, 3)).map(lambda x: x * 2).sum()\n    12\n    \u003e\u003e\u003e def g():\n    ...     for i in [1, 2, 3]:\n    ...         yield i\n    \u003e\u003e\u003e Iter(g()).map(lambda x: x * 2).sum()\n    12\n\n    Iterator\n    \u003e\u003e\u003e Iter(iter([1, 2, 3])).map(lambda x: x * 2).sum()\n    12\n\n    Dict\n    \u003e\u003e\u003e Iter(dict(a=1, b=2)).map(lambda x: x.upper()).collect()\n    ['A', 'B']\n    \u003e\u003e\u003e d = dict(a=1, b=2, c=3)\n    \u003e\u003e\u003e Iter(d.items()).starmap(lambda k, v: v).map(lambda x: x * 2).sum()\n    12\n\nA common error with generators is forgetting to actually evaluate, i.e.,\ncall a generator function. If you do this there's a friendly error\npointing out the mistake:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e def mygen(): yield 123\n    \u003e\u003e\u003e Iter(mygen).collect()\n    Traceback (most recent call last):\n        ...\n    TypeError: It seems you passed a generator function, but you\n    probably intended to pass a generator. Remember to evaluate the\n    function to obtain a generator instance:\n               \n    def mygen():\n        yield 123\n               \n    Iter(mygen)    # ERROR - a generator function object is not iterable\n    Iter(mygen())  # CORRECT - a generator instance is iterable.\n    \u003e\u003e\u003e Iter(mygen()).collect()\n    [123]\n\nInstance of Iter_ are resumable. Once an instance it created, it can\nbe partially iterated in successive calls, like the following example\nshows:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e it = Iter.range(1_000_000)\n    \u003e\u003e\u003e it.take(3).collect()\n    [0, 1, 2]\n    \u003e\u003e\u003e it.take(4).collect()\n    [3, 4, 5, 6]\n    \u003e\u003e\u003e # Consume most of the stream, collect the last few\n    \u003e\u003e\u003e it.consume(999_990).collect()\n    [999997, 999998, 999999]\n\nThis class implements the chaining. However, the module-level functions\nin excitertools_, such as range_, zip_ and so on, also return\ninstances of Iter_, so they allow the chaining to continue. These are\nequivalent:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.range(10).filter(lambda x: x \u003e 7).collect()\n    [8, 9]\n    \u003e\u003e\u003e range(10).filter(lambda x: x \u003e 7).collect()\n    [8, 9]\n\nIt is intended that the module-level functions can act as drop-in\nreplacements for the builtins they wrap:\n\n\u003e\u003e\u003e import builtins\n\u003e\u003e\u003e list(builtins.range(3))\n[0, 1, 2]\n\u003e\u003e\u003e list(range(3))  # This is excitertools.range!\n[0, 1, 2]\n\u003e\u003e\u003e list(Iter.range(3))\n[0, 1, 2]\n\nIn your own code where you might like to use the excitertools_ version of\nrange_ and the other functions, you can just import it and use it to access all the other\ncool stuff:\n\n.. code-block:: python\n\n    # mymodule.py\n    from excitertools import (\n        range,\n        map,\n        filter,\n        reduce,\n        repeat,\n        count,\n        enumerate,\n        zip,\n        ...\n    )\n\n    def func(inputs):\n        data = (\n            map(lambda x: x + 2, inputs)\n                .enumerate()\n                .filter(lambda x: x[1] \u003e 10)\n                ...\n                .collect()\n\n        )\n\nAlternatively, if you don't want to hide the builtins you can do just\nfine with importing this class only, or even importing the module only:\n\n.. code-block:: python\n\n    # mymodule.py - same example as before\n    import excitertools\n\n    def func(inputs):\n        data = (\n            excitertools.Iter(inputs)\n                .map(lambda x: x + 2, inputs)\n                .enumerate()\n                .filter(lambda x: x[1] \u003e 10)\n                ...\n                .collect()\n        )\n\n        # Do something with data\n\nThere are several valuable additions to the standard *itertools* and\nmore-itertools_ functions. These usually involve sources and sinks,\nwhich are ways of getting data into an iterator pipeline, and then\ngetting results out again. In the majority of documentation examples\nshown here, the Iter.collect_ method is used to collect all the\nremaining data on a stream into a list; but in practice this is not\nuseful because large lists consume memory.\n\nIn practice it is more useful to send iterator data to one of these\ncommon sinks:\n\n- files\n- sockets\n- queues\n- HTTP APIs\n- Cloud storage buckets\n- (Ideas for more to add here?)\n\nIter_ has support for these use-cases, both for reading and for writing.\n\n\n\n.. _Iter.next:\n\n\n``Iter.next(self) -\u003e \"T\"``\n==========================\nConvenience function to avoid having to wrap an interator with\nthe `next()` builtin, just to advance it by one step (and return\nthe value). Typical use cases for this might be for tutorials\nand explainers, where you want to show the next value in a\nsequence.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e it = Iter(range(5))\n    \u003e\u003e\u003e it.next()\n    0\n    \u003e\u003e\u003e it.next()\n    1\n    \u003e\u003e\u003e it.collect()\n    [2, 3, 4]\n\n\n\n.. _Iter.register:\n\n\n``@classmethod Iter.register(cls, *func)``\n==========================================\n\nAdd a new method to Iter_. Sure, you could subclass Iter_ to get\nnew chaining features, but it would be neat to let all existing\nIter_ instance just immediately have the new registered function\navailable.\n\nThe new function must take ``iterable`` as the first parameter.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e def up(iterable):\n    ...     for v in iterable:\n    ...         yield v.upper()\n    \u003e\u003e\u003e Iter.register(up)\n    \u003e\u003e\u003e Iter('abc').up().collect()\n    ['A', 'B', 'C']\n    \u003e\u003e\u003e def poly(iterable, a, b, c):\n    ...     # Polynomials a.x^2 + b.x + c\n    ...     for x in iterable:\n    ...         yield a*x**2 + b*x + c\n    \u003e\u003e\u003e Iter.register(poly)\n    \u003e\u003e\u003e Iter(range(-5, 5, 1)).poly(1, -5, 6).collect()\n    [56, 42, 30, 20, 12, 6, 2, 0, 0, 2]\n\nHere's a math round-trip rollercoaster.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e import math\n    \u003e\u003e\u003e def log(iterable):\n    ...     for x in iterable:\n    ...         yield math.log(x)\n    \u003e\u003e\u003e def exp(iterable):\n    ...     for x in iterable:\n    ...         yield math.exp(x)\n    \u003e\u003e\u003e def rnd(iterable):\n    ...     for x in iterable:\n    ...         yield round(x)\n    \u003e\u003e\u003e Iter.register(log, exp, rnd)\n    \u003e\u003e\u003e Iter(range(5)).exp().log().rnd().collect()\n    [0, 1, 2, 3, 4]\n\nThese are silly examples, but hopefully you get the idea.\n\n\n\n.. _Iter.collect:\n\n\n|sink| ``Iter.collect(self, container=list) -\u003e \"List[T]\"``\n==========================================================\n\n\n\nThis is the most common way of \"realizing\" an interable chain\ninto a concrete data structure. It should be the case that this\nis where most of the memory allocation occurs.\n\nThe default container is a list and you'll see throughout this\ndocumentation that most examples produce lists. However,\nany container, and indeed any function, can be used as the sink.\n\nThe basic example:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(3)).collect()\n    [0, 1, 2]\n    \u003e\u003e\u003e Iter(range(3)).collect(tuple)\n    (0, 1, 2)\n\nYou must pay attention to some things. For example, if your\niterable is a string, the characters of the string are what\nget iterated over, and when you collect you'll get a collection\nof those atoms. You can however use ``str`` as your \"container\nfunction\" and that will give you back a string. It's like a join\nwith blank joiner.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').collect()\n    ['a', 'b', 'c']\n    \u003e\u003e\u003e Iter('abc').collect(str)\n    'abc'\n\nWith some types, things get a little more tricky. Take ``bytes``\nfor example:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(b'abc').collect()\n    [97, 98, 99]\n\nYou probably didn't expect to get the integers back right? Anyhow,\nyou can use ``bytes`` as the \"collection container\", just like\nwe did with strings and that will work:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(b'abc').collect(bytes)\n    b'abc'\n    \u003e\u003e\u003e Iter(b'abc').collect(bytearray)\n    bytearray(b'abc')\n\nThe other standard collections also work, here's a set for\ncompleteness.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abcaaaabbbbccc').collect(set) == {'a', 'b', 'c'}\n    True\n\nString subclasses also work.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e class MyString(str): pass\n    \u003e\u003e\u003e out = Iter(MyString('abc')).collect(MyString)\n    \u003e\u003e\u003e out\n    'abc'\n    \u003e\u003e\u003e type(out)\n    \u003cclass 'excitertools.MyString'\u003e\n\n\n\n.. _Iter.open:\n\n\n|cool| |source| ``@classmethod Iter.open(cls, file, mode=\"r\", buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None, ) -\u003e \"Iter\"``\n==============================================================================================================================================================\n\n\n\n\nWrap the ``open()`` builtin precisely, but return an ``Iter``\ninstance to allow function chaining on the result.\n\nI know you're thinking that we should always use a context\nmanager for files. Don't worry, there is one being used\ninternally. When the iterator chain is terminated the underlying\nfile will be closed.\n\n\u003e\u003e\u003e import tempfile\n\u003e\u003e\u003e with tempfile.TemporaryDirectory() as td:\n...     # Put some random text into a temporary file\n...     with open(td + 'text.txt', 'w') as f:\n...         f.writelines(['abc\\n', 'def\\n', 'ghi\\n'])\n...\n...     # Open the file, filter some lines, collect the result\n...     Iter.open(td + 'text.txt').filter(lambda line: 'def' in line).collect()\n['def\\n']\n\nNote that this is a convenience method for *reading* from a file,\nnot for writing. The function signature includes the ``mode``\nparameter for parity with the builtin ``open()`` function, but\nonly reading is supported.\n\n\n\n.. _Iter.read_lines:\n\n\n|source| ``@classmethod Iter.read_lines(cls, stream: IO[str], rewind=True)``\n============================================================================\n\n\n\nRead lines from a file-like object.\n\nFirst, let's put some data in a file. We'll be using that\nfile in the examples that follow.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e import tempfile\n    \u003e\u003e\u003e td = tempfile.TemporaryDirectory()\n    ... # Put some random text into a temporary file\n    \u003e\u003e\u003e with open(td.name + 'text.txt', 'w') as f:\n    ...     f.writelines(['abc\\n', 'def\\n', 'ghi\\n'])\n    ...\n\nUse read_lines to process the file data\n\n.. code-block:: python\n\n    \u003e\u003e\u003e with open(td.name + 'text.txt') as f:\n    ...     Iter.read_lines(f).filter(lambda line: 'def' in line).collect()\n    ['def\\n']\n\nThe ``rewind`` parameter can be used to read sections of a file.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e with open(td.name + 'text.txt') as f:\n    ...     part1 = Iter.read_lines(f).take(1).collect()\n    ...     part2 = Iter.read_lines(f, rewind=False).collect()\n    \u003e\u003e\u003e part1\n    ['abc\\n']\n    \u003e\u003e\u003e part2\n    ['def\\n', 'ghi\\n']\n    \u003e\u003e\u003e td.cleanup()\n\n\n\n.. _Iter.read_bytes:\n\n\n|source| ``@classmethod Iter.read_bytes(cls, stream: IO[bytes], size: Union[Callable[[], int], int] = -1, rewind=True)``\n========================================================================================================================\n\n\n\nThe ``size`` parameter can be used to control how many bytes are\nread for each advancement of the iterator chain. Here we set ``size=1``\nwhich means we'll get back one byte at a time.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e import tempfile\n    \u003e\u003e\u003e td = tempfile.TemporaryDirectory()\n    \u003e\u003e\u003e filename = td.name + 'bytes.bin'\n\nPut some random text into a temporary file:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e with open(filename, 'wb') as f:\n    ...     x = f.write(b'\\x00' * 100)\n    ...\n    \u003e\u003e\u003e with open(filename, 'rb') as f:\n    ...     data = Iter.read_bytes(f, size=1).collect()\n    ...     len(data)\n    100\n    \u003e\u003e\u003e with open(filename, 'rb') as f:\n    ...     data = Iter.read_bytes(f).collect()\n    ...     len(data)\n    1\n\nA little more ambitious. Because ``size`` is a callable, we can use\na ``deque`` and a ``side_effect`` to pass information back into\nthe reader to control how many bytes are read in each chunk.\n\nIn this example we're reading 1 byte at a time. In a real example\nyou might have a sequence of headers and bodies, where headers\ngive size information about how many bytes are in the body\ncorresponding to that header. Then you can precisely read\neach body in sequence.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from collections import deque\n    \u003e\u003e\u003e read_sizes = deque([1])\n    \u003e\u003e\u003e with open(filename, 'rb') as f:\n    ...     data = (\n    ...         Iter\n    ...             .read_bytes(f, size=lambda: read_sizes.popleft())\n    ...             .side_effect(lambda bytes: read_sizes.append(1))\n    ...             .collect()\n    ...     )\n    ...     len(data)\n    100\n\nThe ``rewind`` parameter can be used to read sections of a file.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e with open(filename, 'rb') as f:\n    ...     part1 = Iter.read_bytes(f, size=10).take(1).collect()\n    ...     part2 = Iter.read_bytes(f, rewind=False).collect()\n    \u003e\u003e\u003e part1\n    [b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00']\n    \u003e\u003e\u003e len(part2[0])\n    90\n    \u003e\u003e\u003e td.cleanup()\n\n\n\n.. _Iter.write_text_to_stream:\n\n\n|sink| ``Iter.write_text_to_stream(self, stream: IO[str], insert_newlines=True, flush=True)``\n=============================================================================================\n\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e import tempfile\n    \u003e\u003e\u003e td = tempfile.TemporaryDirectory()\n    \u003e\u003e\u003e filename = td.name + 'text.txt'\n\n    \u003e\u003e\u003e data = ['a', 'b', 'c']\n    \u003e\u003e\u003e with open(filename, 'w') as f:\n    ...     Iter(data).map(str.upper).write_text_to_stream(f)\n    ...     with open(filename) as f2:\n    ...         Iter.read_lines(f2).concat()\n    'A\\nB\\nC'\n\nIf some prior step adds newlines, or more commonly, newlines\noriginate with a data source and are simply carried through the\nprocessing chain unaltered, disable the insertion of newlines:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e with open(filename, 'w') as f:\n    ...     Iter(data).map(str.upper).write_text_to_stream(f, insert_newlines=False)\n    ...     with open(filename) as f2:\n    ...         Iter.read_lines(f2).concat()\n    'ABC'\n\nMultiple successive writes may be slowed down by the default\n``flush=True`` parameter. In this case you can delay flushing until\neverything has been written.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e with open(filename, 'w') as f:\n    ...     Iter(data).map(str.upper).write_text_to_stream(f, flush=False)\n    ...     Iter(data).map(str.upper).write_text_to_stream(f, flush=False)\n    ...     Iter(data).map(str.upper).write_text_to_stream(f, flush=True)\n    ...     with open(filename) as f2:\n    ...         Iter.read_lines(f2).concat()\n    'A\\nB\\nCA\\nB\\nCA\\nB\\nC'\n    \u003e\u003e\u003e td.cleanup()\n\n\n\n.. _Iter.write_bytes_to_stream:\n\n\n|sink| ``Iter.write_bytes_to_stream(self, stream: IO[bytes], flush=True)``\n==========================================================================\n\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e import tempfile\n    \u003e\u003e\u003e td = tempfile.TemporaryDirectory()\n    \u003e\u003e\u003e filename = td.name + 'bytes.bin'\n    \u003e\u003e\u003e data = [b'a', b'b', b'c']\n    \u003e\u003e\u003e with open(filename, 'wb') as f:\n    ...     Iter(data).map(lambda x: x * 2 ).write_bytes_to_stream(f)\n    ...     with open(filename, 'rb') as f2:\n    ...         Iter.read_bytes(f2).collect()\n    [b'aabbcc']\n    \u003e\u003e\u003e with open(filename, 'wb') as f:\n    ...     Iter(data).map(lambda x: x * 2 ).write_bytes_to_stream(f)\n    ...     with open(filename, 'rb') as f2:\n    ...         Iter.read_bytes(f2).concat(b'')\n    b'aabbcc'\n    \u003e\u003e\u003e with open(filename, 'wb') as f:\n    ...     Iter(data).map(lambda x: x * 2 ).write_bytes_to_stream(f)\n    ...     with open(filename, 'rb') as f2:\n    ...         Iter.read_bytes(f2, size=1).collect()\n    [b'a', b'a', b'b', b'b', b'c', b'c']\n    \u003e\u003e\u003e with open(filename, 'wb') as f:\n    ...     Iter(data).map(lambda x: x * 2 ).write_bytes_to_stream(f)\n    ...     with open(filename, 'rb') as f2:\n    ...         Iter.read_bytes(f2, size=2).map(bytes.decode).collect()\n    ['aa', 'bb', 'cc']\n\nFlushing can be delayed if multiple parts are to be written.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e with open(filename, 'wb') as f:\n    ...     it = Iter(data)\n    ...     it.map(lambda x: x * 2 ).take(2).write_bytes_to_stream(f, flush=False)\n    ...     it.map(lambda x: x * 2 ).write_bytes_to_stream(f, flush=True)\n    ...     with open(filename, 'rb') as f2:\n    ...         Iter.read_bytes(f2, size=2).map(bytes.decode).collect()\n    ['aa', 'bb', 'cc']\n    \u003e\u003e\u003e td.cleanup()\n\n\n\n.. _Iter.write_to_file:\n\n\n|cool| |sink| ``Iter.write_to_file(self, file, mode=\"w\", buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None, )``\n===============================================================================================================================================\n\n\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e import tempfile\n    \u003e\u003e\u003e with tempfile.TemporaryDirectory() as td:\n    ...     # Put some random text into a temporary file\n    ...     with open(td + 'text.txt', 'w') as f:\n    ...         f.writelines(['abc\\n', 'def\\n', 'ghi\\n'])\n    ...\n    ...     # Open the file, transform, write out to new file.\n    ...     Iter.open(td + 'text.txt').map(str.upper).write_to_file(td + 'test2.txt')\n    ...     # Read the new file, for the test\n    ...     Iter.open(td + 'test2.txt').collect()\n    ['ABC\\n', 'DEF\\n', 'GHI\\n']\n\n\n\n.. _Iter.range:\n\n\n|source| ``@classmethod Iter.range(cls, *args) -\u003e \"Iter[int]\"``\n===============================================================\n\n\n\nThe ``range`` function you all know and love.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.range(3).collect()\n    [0, 1, 2]\n    \u003e\u003e\u003e Iter.range(0).collect()\n    []\n\n\n\n.. _Iter.zip:\n\n\n``Iter.zip(self, *iterables: Any) -\u003e \"Iter[Tuple[T, ...]]\"``\n============================================================\n\n\nThe ``zip`` function you all know and love. The only thing to\nnote here is that the first iterable is really what the Iter_\ninstance is wrapping. The Iter.zip_ invocation brings in the\nother iterables.\n\nMake an Iter_ instance, then call ``zip`` on that.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('caleb').zip(range(10)).collect()\n    [('c', 0), ('a', 1), ('l', 2), ('e', 3), ('b', 4)]\n\nUse a classmethod to get an infinite stream using Iter.count_\nand zip against that with more finite iterators.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.count().zip(range(5), range(3, 100, 2)).collect()\n    [(0, 0, 3), (1, 1, 5), (2, 2, 7), (3, 3, 9), (4, 4, 11)]\n\nIt takes a few minutes to get used to that but feels comfortable\npretty quickly.\n\nIter.take_ can be used to stop infinite zip sequences:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('caleb').cycle().enumerate().take(8).collect()\n    [(0, 'c'), (1, 'a'), (2, 'l'), (3, 'e'), (4, 'b'), (5, 'c'), (6, 'a'), (7, 'l')]\n\nWhile we're here (assuming you worked through the previous\nexample), note the difference if you switch the order of the\nIter.cycle_ and Iter.enumerate_ calls:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('caleb').enumerate().cycle().take(8).collect()\n    [(0, 'c'), (1, 'a'), (2, 'l'), (3, 'e'), (4, 'b'), (0, 'c'), (1, 'a'), (2, 'l')]\n\nIf you understand how this works, everything else in _excitertools_\nwill be intuitive to use.\n\n\n\n.. _Iter.any:\n\n\n|sink| ``Iter.any(self) -\u003e \"bool\"``\n===================================\n\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([0, 0, 0]).any()\n    False\n    \u003e\u003e\u003e Iter([0, 0, 1]).any()\n    True\n    \u003e\u003e\u003e Iter([]).any()\n    False\n\n\n\n.. _Iter.all:\n\n\n|sink| ``Iter.all(self) -\u003e \"bool\"``\n===================================\n\n\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([0, 0, 0]).all()\n    False\n    \u003e\u003e\u003e Iter([0, 0, 1]).all()\n    False\n    \u003e\u003e\u003e Iter([1, 1, 1]).all()\n    True\n\nNow pay attention:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([]).all()\n    True\n\nThis behaviour has some controversy around it, but that's how the\n``all()`` builtin works so that's what we do too. The way to\nthink about what ``all()`` does is this: it returns False if there\nis at least one element that is falsy.  Thus, if there are no elements\nit follows that there are no elements that are falsy and that's why\n``all([]) == True``.\n\n\n\n.. _Iter.enumerate:\n\n\n``Iter.enumerate(self) -\u003e \"Iter[Tuple[int, T]]\"``\n=================================================\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').enumerate().collect()\n    [(0, 'a'), (1, 'b'), (2, 'c')]\n    \u003e\u003e\u003e Iter([]).enumerate().collect()\n    []\n\n\n\n.. _Iter.dict:\n\n\n``Iter.dict(self) -\u003e \"Dict\"``\n=============================\n\nIn regular Python a dict can be constructed through an iterable\nof tuples:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e dict([('a', 0), ('b', 1)])                  \n    {'a': 0, 'b': 1}\n\nIn *excitertools* we prefer chaining so this method is a shortcut\nfor that:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e d = Iter('abc').zip(count()).dict()\n    \u003e\u003e\u003e assert d == {'a': 0, 'b': 1, 'c': 2}\n\n\n\n.. _Iter.map:\n\n\n``Iter.map(self, func: Union[Callable[..., C], str]) -\u003e \"Iter[C]\"``\n===================================================================\n\nThe ``map`` function you all know and love.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').map(str.upper).collect()\n    ['A', 'B', 'C']\n    \u003e\u003e\u003e Iter(['abc', 'def']).map(str.upper).collect()\n    ['ABC', 'DEF']\n\nUsing lambdas might seem convenient but in practice it turns\nout that they make code difficult to read:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e result = Iter('caleb').map(lambda x: (x, ord(x))).dict()\n    \u003e\u003e\u003e assert result == {'a': 97, 'b': 98, 'c': 99, 'e': 101, 'l': 108}\n\nIt's recommended that you make a separate function instead:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e def f(x):\n    ...     return x, ord(x)\n    \u003e\u003e\u003e result = Iter('caleb').map(f).dict()\n    \u003e\u003e\u003e assert result == {'a': 97, 'b': 98, 'c': 99, 'e': 101, 'l': 108}\n\nI know many people prefer anonymous functions (often on\nphilosphical grounds) but in practice it's just easier to make\na separate, named function.\n\nI've experimented with passing a string into the map, and using\n``eval()`` to make a lambda internally. This simplifies the code\nvery slightly, at the cost of using strings-as-code. I'm pretty\nsure this feature will be removed so don't use it.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e result = Iter('caleb').map('x, ord(x)').dict()\n    \u003e\u003e\u003e assert result == {'a': 97, 'b': 98, 'c': 99, 'e': 101, 'l': 108}\n\n\n\n.. _Iter.filter:\n\n\n``Iter.filter(self, function: \"Optional[Callable[[T], bool]]\" = None) -\u003e \"Iter[T]\"``\n====================================================================================\n\nThe ``map`` function you all know and love.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('caleb').filter(lambda x: x in 'aeiou').collect()\n    ['a', 'e']\n\nThere is a slight difference between this method signature and\nthe builtin ``filter``:  how the identity function is handled.\nThis is a consquence of chaining. In the function signature above\nit is possible for us to give the ``function`` parameter a\ndefault value of ``None`` because the parameter appears towards\nthe end of the parameter list. Last, in fact.  In the\n`builtin filter signature \u003chttps://docs.python.org/3/library/functions.html#filter\u003e`_\nit doesn't allow for this because the predicate parameter appears\nfirst.\n\nThis is a long way of saying: if you just want to filter out\nfalsy values, no parameter is needed:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([0, 1, 0, 0, 0, 1, 1, 1, 0, 0]).filter().collect()\n    [1, 1, 1, 1]\n\nUsing the builtin, you'd have to do ``filter(None, iterable)``.\n\nYou'll find that Iter.map_ and Iter.filter_\n(and Iter.reduce_, up next) work together very nicely:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e def not_eve(x):\n    ...    return x != 'eve'\n    \u003e\u003e\u003e Iter(['bob', 'eve', 'alice']).filter(not_eve).map(str.upper).collect()\n    ['BOB', 'ALICE']\n\nThe long chains get unwieldy so let's rewrite that:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e (\n    ...     Iter(['bob', 'eve', 'alice'])\n    ...         .filter(not_eve)\n    ...         .map(str.upper)\n    ...         .collect()\n    ... )\n    ['BOB', 'ALICE']\n\n\n\n.. _Iter.starfilter:\n\n\n|cool| ``Iter.starfilter(self, function: \"Optional[Callable[[T, ...], bool]]\" = None) -\u003e \"Iter[T]\"``\n====================================================================================================\n\n\nLike Iter.filter_, but arg unpacking in lambdas will work.\n\nWith the normal ``filter``, this fails:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('caleb').enumerate().filter(lambda i, x: i \u003e 2).collect()\n    Traceback (most recent call last):\n        ...\n    TypeError: \u003clambda\u003e() missing 1 required positional argument: 'x'\n\nThis is a real buzzkill. ``starfilter`` is very similar to\n``starmap`` in that tuples are unpacked when calling the function:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('caleb').enumerate().starfilter(lambda i, x: i \u003e 2).collect()\n    [(3, 'e'), (4, 'b')]\n\n\n\n.. _Iter.filter_gt:\n\n\n``Iter.filter_gt(self, value) -\u003e \"Iter[T]\"``\n============================================\n\nConvenience method\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1,2,3]).filter_gt(1).collect()\n    [2, 3]\n\n\n\n.. _Iter.filter_ge:\n\n\n``Iter.filter_ge(self, value) -\u003e \"Iter[T]\"``\n============================================\n\nConvenience method\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1,2,3]).filter_ge(2).collect()\n    [2, 3]\n\n\n\n.. _Iter.filter_lt:\n\n\n``Iter.filter_lt(self, value) -\u003e \"Iter[T]\"``\n============================================\n\nConvenience method\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1,2,3]).filter_lt(3).collect()\n    [1, 2]\n\n\n.. _Iter.filter_le:\n\n\n``Iter.filter_le(self, value) -\u003e \"Iter[T]\"``\n============================================\n\nConvenience method\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1,2,3]).filter_le(2).collect()\n    [1, 2]\n\n\n.. _Iter.filter_eq:\n\n\n``Iter.filter_eq(self, value) -\u003e \"Iter[T]\"``\n============================================\n\nConvenience method\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1,2,3]).filter_eq(2).collect()\n    [2]\n\n\n.. _Iter.filter_ne:\n\n\n``Iter.filter_ne(self, value) -\u003e \"Iter[T]\"``\n============================================\n\nConvenience method\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1,2,3]).filter_ne(2).collect()\n    [1, 3]\n\n\n.. _Iter.filter_in:\n\n\n``Iter.filter_in(self, value: Sized) -\u003e \"Iter[T]\"``\n===================================================\n\nConvenience method for membership testing. Note that the value\nparameter must be at least ``Sized`` because it gets reused\nover and over for each pass of the iterator chain. For example,\npassing in things like ``range()`` will not work properly because\nit will become progressively exhausted.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1,2,3]).filter_in([2, 3, 4, 5]).collect()\n    [2, 3]\n    \u003e\u003e\u003e Iter([1,2,3]).filter_in(range(2, 8).collect()).collect()\n    [2, 3]\n    \u003e\u003e\u003e Iter([1,2,3]).filter_in({2, 3, 4, 5}).collect()\n    [2, 3]\n    \u003e\u003e\u003e Iter([1,2,3]).filter_in(dict.fromkeys({2, 3, 4, 5})).collect()\n    [2, 3]\n\n\n.. _Iter.filter_ni:\n\n\n``Iter.filter_ni(self, value) -\u003e \"Iter[T]\"``\n============================================\n\nConvenience method for membership testing. Note that the value\nparameter must be at least ``Sized`` because it gets reused\nover and over for each pass of the iterator chain. For example,\npassing in things like ``range()`` will not work properly because\nit will become progressively exhausted.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1,2,3]).filter_ni([2, 3, 4, 5]).collect()\n    [1]\n    \u003e\u003e\u003e Iter([1,2,3]).filter_ni(range(2, 8).collect()).collect()\n    [1]\n    \u003e\u003e\u003e Iter([1,2,3]).filter_ni({2, 3, 4, 5}).collect()\n    [1]\n    \u003e\u003e\u003e Iter([1,2,3]).filter_ni(dict.fromkeys({2, 3, 4, 5})).collect()\n    [1]\n\n\n.. _Iter.reduce:\n\n\n|sink| ``Iter.reduce(self, func: Callable[..., T], *args) -\u003e \"T\"``\n==================================================================\n\n\nThe ``reduce`` function you all know and...hang on, actually\n``reduce`` is rather unloved. In the past I've found it very complex\nto reason about, when looking at a bunch of nested function calls\nin typical ``itertools`` code. Hopefully iterable chaining makes\nit easier to read code that uses ``reduce``?\n\nLet's check, does this make sense?\n\n.. code-block:: python\n\n    \u003e\u003e\u003e payments = [\n    ...     ('bob', 100),\n    ...     ('alice', 50),\n    ...     ('eve', -100),\n    ...     ('bob', 19.95),\n    ...     ('bob', -5.50),\n    ...     ('eve', 11.95),\n    ...     ('eve', 200),\n    ...     ('alice', -45),\n    ...     ('alice', -67),\n    ...     ('bob', 1.99),\n    ...     ('alice', 89),\n    ... ]\n    \u003e\u003e\u003e (\n    ...     Iter(payments)\n    ...         .filter(lambda entry: entry[0] == 'bob')\n    ...         .map(lambda entry: entry[1])\n    ...         .reduce(lambda total, value: total + value, 0)\n    ... )\n    116.44\n\nI intentionally omitted comments above so that you can try the\n\"readability experiment\", but in practice you would definitely\nwant to add some comments on these chains:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e (\n    ...     # Iterate over all payments\n    ...     Iter(payments)\n    ...         # Only look at bob's payments\n    ...         .filter(lambda entry: entry[0] == 'bob')\n    ...         # Extract the value of the payment\n    ...         .map(lambda entry: entry[1])\n    ...         # Add all those payments together\n    ...         .reduce(lambda total, value: total + value, 0)\n    ... )\n    116.44\n\n``reduce`` is a quite crude low-level tool. In many cases you'll\nfind that there are other functions and methods better suited\nto the situations you'll encounter most often. For example,\nthere is already Iter.sum_ if you just want to add up numbers,\nand it's much easier to use Iter.groupby_ for grouping than\nto try to make that work with Iter.reduce_. You *can* make it\nwork but it'll be easier to use Iter.groupby_.\n\n\n\n.. _Iter.starreduce:\n\n\n|sink| ``Iter.starreduce(self, function: Callable[..., T], initializer=0) -\u003e \"T\"``\n==================================================================================\n\n\nIter.starreduce_ is the same as Iter.reduce_ except that args are\nstar-unpacked when passed into ``function``. This is frequently\nmore convenient than the default behaviour.\n\nWe can see this using the same example shown for Iter.reduce_.\nThe star unpacking makes it easier to just do the filtering\ndirectly inside the reducer function.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e payments = [\n    ...     ('bob', 100),\n    ...     ('alice', 50),\n    ...     ('eve', -100),\n    ...     ('bob', 19.95),\n    ...     ('bob', -5.50),\n    ...     ('eve', 11.95),\n    ...     ('eve', 200),\n    ...     ('alice', -45),\n    ...     ('alice', -67),\n    ...     ('bob', 1.99),\n    ...     ('alice', 89),\n    ... ]\n    \u003e\u003e\u003e (\n    ...     Iter(payments)\n    ...         .starreduce(\n    ...             lambda tot, name, value: tot + value if name == 'bob' else tot,\n    ...             0\n    ...         )\n    ... )\n    116.44\n\nThis is how that looks if you avoid a lambda:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e def f(tot, name, value):\n    ...     if name == 'bob':\n    ...         return tot + value\n    ...     else:\n    ...         return tot\n    \u003e\u003e\u003e Iter(payments).starreduce(f)\n    116.44\n\n\n\n.. _Iter.sum:\n\n\n|sink| ``Iter.sum(self)``\n=========================\n\n\nExactly what you expect:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(10)).sum()\n    45\n\n\n\n.. _Iter.concat:\n\n\n|sink| ``Iter.concat(self, glue: AnyStr = \"\") -\u003e \"AnyStr\"``\n===========================================================\n\n\n\nJoining strings (and bytes).\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(['hello', 'there']).concat()\n    'hellothere'\n    \u003e\u003e\u003e Iter(['hello', 'there']).concat(' ')\n    'hello there'\n    \u003e\u003e\u003e Iter(['hello', 'there']).concat(',')\n    'hello,there'\n    \u003e\u003e\u003e Iter([b'hello', b'there']).concat(b',')\n    b'hello,there'\n\n\n\n.. _Iter.insert:\n\n\n``Iter.insert(self, glue: C) -\u003e \"Iter[Union[C, T]]\"``\n=====================================================\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').insert('x').collect()\n    ['a', 'x', 'b', 'x', 'c']\n    \u003e\u003e\u003e Iter('abc').insert('x').concat('')\n    'axbxc'\n    \u003e\u003e\u003e Iter([]).insert('x').collect()\n    []\n\n\n\n.. _Iter.count:\n\n\n|source| ``@classmethod Iter.count(cls, *args) -\u003e \"Iter[int]\"``\n===============================================================\n\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.count().take(3).collect()\n    [0, 1, 2]\n    \u003e\u003e\u003e Iter.count(100).take(3).collect()\n    [100, 101, 102]\n    \u003e\u003e\u003e Iter.count(100, 2).take(3).collect()\n    [100, 102, 104]\n\n\n\n.. _Iter.cycle:\n\n\n|inf| ``Iter.cycle(self) -\u003e Self``\n==================================\n\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').cycle().take(8).collect()\n    ['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b']\n    \u003e\u003e\u003e Iter('abc').cycle().take(8).concat('')\n    'abcabcab'\n\n\n\n.. _Iter.repeat:\n\n\n|source| |inf| ``@classmethod Iter.repeat(cls, elem: C, times=None) -\u003e \"Iter[C]\"``\n==================================================================================\n\n\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.repeat('c', times=3).collect()\n    ['c', 'c', 'c']\n\n\n\n.. _Iter.accumulate:\n\n\n``Iter.accumulate(self, func=None, *, initial=None)``\n=====================================================\nReference `itertools.accumulate \u003chttps://docs.python.org/3/library/itertools.html#itertools.accumulate\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3, 4, 5]).accumulate().collect()\n    [1, 3, 6, 10, 15]\n    \u003e\u003e\u003e if sys.version_info \u003e= (3, 8):\n    ...     out = Iter([1, 2, 3, 4, 5]).accumulate(initial=100).collect()\n    ...     assert out == [100, 101, 103, 106, 110, 115]\n    \u003e\u003e\u003e Iter([1, 2, 3, 4, 5]).accumulate(operator.mul).collect()\n    [1, 2, 6, 24, 120]\n\n    Example from the itertools docs:\n    Amortize a 5% loan of 1000 with 10 annual payments of 90\n    \u003e\u003e\u003e update = lambda balance, payment: round(balance * 1.05) - payment\n\n    This is written in the itertools docs:\n    \u003e\u003e\u003e list(accumulate(repeat(90, 10), update, initial=1_000))                 \n\n    This is using excitertools:\n    \u003e\u003e\u003e repeat(90, 10).accumulate(update, initial=1000).collect()\n    [1000, 960, 918, 874, 828, 779, 728, 674, 618, 559, 497]\n\n\n\n.. _Iter.chain:\n\n\n``Iter.chain(self, *iterables: Iterable[T]) -\u003e Self``\n=====================================================\nChain together multiple iterables. This is a replacement for the\nitertools ``chain`` function.  This version returns an instance of\nIter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('ABC').chain('DEF').collect()\n    ['A', 'B', 'C', 'D', 'E', 'F']\n    \u003e\u003e\u003e Iter('AB').chain('CD', 'EF').collect()\n    ['A', 'B', 'C', 'D', 'E', 'F']\n    \u003e\u003e\u003e Iter('ABC').chain().collect()\n    ['A', 'B', 'C']\n\n\n\n.. _Iter.chain_from_iterable:\n\n\n``Iter.chain_from_iterable(self) -\u003e Self``\n==========================================\nThis is similar to Iter.chain_ but it takes a single iterable\nof iterables. This is a replacement for the itertools\n``chain.from_iterable`` function.  This version returns an\ninstance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(['ABC', 'DEF']).chain_from_iterable().collect()\n    ['A', 'B', 'C', 'D', 'E', 'F']\n    \u003e\u003e\u003e Iter([range(3), range(4)]).chain_from_iterable().collect()\n    [0, 1, 2, 0, 1, 2, 3]\n\n\n\n.. _Iter.compress:\n\n\n``Iter.compress(self, selectors) -\u003e Self``\n==========================================\nReplacement for the itertools ``compress`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('ABCDEF').compress([1, 0, 1, 0, 1, 1]).collect()\n    ['A', 'C', 'E', 'F']\n\n\n\n.. _Iter.dropwhile:\n\n\n``Iter.dropwhile(self, pred) -\u003e Self``\n======================================\n\nReplacement for the itertools ``dropwhile`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').dropwhile(lambda x: x \u003c 'c').collect()\n    ['c']\n\n\n\n.. _Iter.filterfalse:\n\n\n``Iter.filterfalse(self, pred) -\u003e Self``\n========================================\n\nReplacement for the itertools ``filterfalse`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').filterfalse(lambda x: x \u003c 'c').collect()\n    ['c']\n\n`filterfalse` is useful when you want to exclude elements based\non a membership test.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e stopwords = {'the', 'and', 'or', 'but'}\n    \u003e\u003e\u003e text = 'the quick brown fox jumps over the lazy dog'.split()\n    \u003e\u003e\u003e Iter(text).filterfalse(stopwords.__contains__).collect()\n    ['quick', 'brown', 'fox', 'jumps', 'over', 'lazy', 'dog']\n\n\n\n.. _Iter.groupby:\n\n\n``Iter.groupby(self, key=None) -\u003e \"Iter[Tuple[Any, Iter[T]]]\"``\n===============================================================\n\nReplacement for the itertools ``groupby`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nThe grouper is also an instance of Iter_, mainly because that\nallows many different operations to be performed on the group\nas well as realizations like `.collect(...)` or `.ilen()`.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from collections import Counter\n    \u003e\u003e\u003e (\n    ...   Iter('AAAABBBCCDAABBB')\n    ...   .groupby()\n    ...   .starmap(lambda key, grouper: (key, grouper.ilen()))\n    ...   .collect()\n    ... )\n    [('A', 4), ('B', 3), ('C', 2), ('D', 1), ('A', 2), ('B', 3)]\n\nNote that it doesn't do a groupby in the sense that you would\nnormally expect from a database query. It's more like a\n\"consecutive groupby\".\n\nTo group up everything, it needs a bit more thinking:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e def add_group_counts(d: dict, k, v):\n    ...     d[k] = d.get(k, 0) + v\n    ...     return d\n    \u003e\u003e\u003e (\n    ...   Iter('AAAABBBCCDAABBB')\n    ...   .groupby()\n    ...   .starmap(lambda key, grouper: (key, grouper.ilen()))\n    ...   .starreduce(add_group_counts, {})\n    ... )\n    {'A': 6, 'B': 6, 'C': 2, 'D': 1}\n\nIn this specific example, we have merely reimplemented `collections.Counter`.\n\n\n\n.. _Iter.islice:\n\n\n``Iter.islice(self, *args) -\u003e Self``\n====================================\n\nReplacement for the itertools ``islice`` function.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abcdef').islice(2).collect()\n    ['a', 'b']\n    \u003e\u003e\u003e Iter('abcdef').islice(2, 4).collect()\n    ['c', 'd']\n\n\n\n.. _Iter.starmap:\n\n\n``Iter.starmap(self, func) -\u003e Self``\n====================================\n\nReplacement for the itertools ``starmap`` function.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([(0, 1), (2, 3)]).starmap(operator.add).collect()\n    [1, 5]\n    \u003e\u003e\u003e Iter([(0, 1), (2, 3)]).starmap(lambda x, y: x + y).collect()\n    [1, 5]\n\n\n\n.. _Iter.takewhile:\n\n\n``Iter.takewhile(self, pred) -\u003e Self``\n======================================\n\nReplacement for the itertools ``takewhile`` function.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').takewhile(lambda x: x \u003c 'c').collect()\n    ['a', 'b']\n\n\n\n.. _Iter.tee:\n\n\n``Iter.tee(self, n=2) -\u003e \"Tuple[Self, ...]\"``\n=============================================\n\nReplacement for the itertools ``tee`` function.  This version returns\ninstances of Iter_ to allow further iterable chaining.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e a, b = Iter('abc').tee()\n    \u003e\u003e\u003e a.collect()\n    ['a', 'b', 'c']\n    \u003e\u003e\u003e b.collect()\n    ['a', 'b', 'c']\n\n\n\n.. _Iter.zip_longest:\n\n\n``Iter.zip_longest(self, *iterables, fillvalue=None) -\u003e Self``\n==============================================================\n\nReplacement for the itertools ``zip_longest`` function.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').zip_longest('123').collect()\n    [('a', '1'), ('b', '2'), ('c', '3')]\n    \u003e\u003e\u003e Iter('abcdef').zip_longest('123', fillvalue='x').collect()\n    [('a', '1'), ('b', '2'), ('c', '3'), ('d', 'x'), ('e', 'x'), ('f', 'x')]\n\n\n\n.. _Iter.chunked:\n\n\n``Iter.chunked(self, n: int) -\u003e Self``\n======================================\n\nReplacement for the more-itertools ``chunked`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.chunked \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abcdef').chunked(3).collect()\n    [['a', 'b', 'c'], ['d', 'e', 'f']]\n    \u003e\u003e\u003e Iter('abcde').chunked(3).collect()\n    [['a', 'b', 'c'], ['d', 'e']]\n\n\n\n.. _Iter.ichunked:\n\n\n``Iter.ichunked(self, n: int) -\u003e Self``\n=======================================\n\nReplacement for the more-itertools ``ichunked`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nThis version differs from Iter.chunked_ in that it returns\nan iterator of iterators rather than an iterator of lists.\n\nReference: `more_itertools.ichunked \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ichunked\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('aabbcc').ichunked(3).map(list).collect()\n    [['a', 'a', 'b'], ['b', 'c', 'c']]\n    \u003e\u003e\u003e Iter('aabbcc').ichunked(3).map(tuple).collect()\n    [('a', 'a', 'b'), ('b', 'c', 'c')]\n    \u003e\u003e\u003e out = Iter('aabbcc').ichunked(3).map(set).collect()\n    \u003e\u003e\u003e out == [{'a', 'b'}, {'b', 'c'}]\n    True\n\n\n\n.. _Iter.sliced:\n\n\n|source| ``@classmethod Iter.sliced(cls, seq: Sequence, n: int) -\u003e Self``\n=========================================================================\n\n\n\nReplacement for the more-itertools ``sliced`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.sliced \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sliced\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.sliced('abcdef', 3).collect()\n    ['abc', 'def']\n\n\n\n.. _Iter.constrained_batches:\n\n\n``Iter.constrained_batches(self, max_size: int, max_count=None, get_len=len, strict=True) -\u003e Self``\n===================================================================================================\n\nReplacement for the more-itertools ``constrained_batches`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.constrained_batches \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.constrained_batches\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = [b'12345', b'123', b'12345678', b'1', b'1', b'12', b'1']\n    \u003e\u003e\u003e Iter(iterable).constrained_batches(10).collect()\n    [(b'12345', b'123'), (b'12345678', b'1', b'1'), (b'12', b'1')]\n    \u003e\u003e\u003e Iter(iterable).constrained_batches(10, max_count=2).collect()\n    [(b'12345', b'123'), (b'12345678', b'1'), (b'1', b'12'), (b'1',)]\n\n\n\n.. _Iter.distribute:\n\n\n``Iter.distribute(self, n: int) -\u003e Self``\n=========================================\n\nReplacement for the more-itertools ``distribute`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.distribute \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distribute\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e group_1, group_2, group_3 = Iter('abcdef').distribute(3).collect()\n    \u003e\u003e\u003e group_1.collect()\n    ['a', 'd']\n    \u003e\u003e\u003e group_2.collect()\n    ['b', 'e']\n    \u003e\u003e\u003e group_3.collect()\n    ['c', 'f']\n\nNote that each of the returned iterables is an instance of Iter_, so chaining works.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e groups = Iter('abcdef').distribute(3).collect()\n    \u003e\u003e\u003e groups[0].map(str.upper).collect()\n    ['A', 'D']\n\n\n\n.. _Iter.divide:\n\n\n``Iter.divide(self, n: int) -\u003e Self``\n=====================================\n\nReplacement for the more-itertools ``divide`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.divide \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.divide\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abcdef').divide(3).map(list).collect()\n    [['a', 'b'], ['c', 'd'], ['e', 'f']]\n    \u003e\u003e\u003e Iter('abc').divide(5).map(list).collect()\n    [['a'], ['b'], ['c'], [], []]\n\n\n\n.. _Iter.split_at:\n\n\n``Iter.split_at(self, pred: Callable[[T], bool], maxsplit=-1, keep_separator=False) -\u003e Self``\n=============================================================================================\n\nReplacement for the more-itertools ``split_at`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.split_at \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_at\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abcdef').split_at(lambda char: char == 'd').collect()\n    [['a', 'b', 'c'], ['e', 'f']]\n    \u003e\u003e\u003e Iter.range(10).split_at(lambda x: x % 2 == 1).collect()\n    [[0], [2], [4], [6], [8], []]\n    \u003e\u003e\u003e Iter.range(10).split_at(lambda x: x % 2 == 1, maxsplit=2).collect()\n    [[0], [2], [4, 5, 6, 7, 8, 9]]\n    \u003e\u003e\u003e Iter(\"abcdcba\").split_at(lambda x: x == 'b', keep_separator=True).collect()\n    [['a'], ['b'], ['c', 'd', 'c'], ['b'], ['a']]\n\n\n\n.. _Iter.split_before:\n\n\n``Iter.split_before(self, pred: Callable[[T], bool], maxsplit=-1) -\u003e Self``\n===========================================================================\n\nReplacement for the more-itertools ``split_before`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.split_before \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_before\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('OneTwo').split_before(str.isupper).collect()\n    [['O', 'n', 'e'], ['T', 'w', 'o']]\n    \u003e\u003e\u003e Iter(range(10)).split_before(lambda x: x % 3 == 0, maxsplit=2).collect()\n    [[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]\n\n\n\n.. _Iter.split_after:\n\n\n``Iter.split_after(self, pred: Callable[[T], bool], maxsplit=-1) -\u003e Self``\n==========================================================================\n\nReplacement for the more-itertools ``split_after`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.split_after \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_after\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('one1two2').split_after(str.isdigit).collect()\n    [['o', 'n', 'e', '1'], ['t', 'w', 'o', '2']]\n    \u003e\u003e\u003e Iter(range(10)).split_after(lambda x: x % 3 == 0, maxsplit=2).collect()\n    [[0], [1, 2, 3], [4, 5, 6, 7, 8, 9]]\n\n\n\n.. _Iter.split_into:\n\n\n``Iter.split_into(self, sizes: Iterable[int]) -\u003e Self``\n=======================================================\n\nReplacement for the more-itertools ``split_into`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.split_into \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_into\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abcdef').split_into([2, 3, 1]).collect()\n    [['a', 'b'], ['c', 'd', 'e'], ['f']]\n    \u003e\u003e\u003e Iter('abcdef').split_into(iter([2, 3, 1])).collect()\n    [['a', 'b'], ['c', 'd', 'e'], ['f']]\n    \u003e\u003e\u003e Iter('abcdef').split_into([2, 3]).collect()\n    [['a', 'b'], ['c', 'd', 'e']]\n\n\n\n.. _Iter.split_when:\n\n\n``Iter.split_when(self, pred: Callable[[T], bool], maxsplit=-1) -\u003e Self``\n=========================================================================\n\nReplacement for the more-itertools ``split_when`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.split_when \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_when\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3, 3, 2, 5, 2, 4, 2]).split_when(lambda x, y: x \u003e y).collect()\n    [[1, 2, 3, 3], [2, 5], [2, 4], [2]]\n\n\n\n.. _Iter.bucket:\n\n\n``Iter.bucket(self, key, validator=None)``\n==========================================\n\nThis is the basic example, copied from the more-itertools\ndocs:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = ['a1', 'b1', 'c1', 'a2', 'b2', 'c2', 'b3']\n    \u003e\u003e\u003e b = Iter(iterable).bucket(key=lambda x: x[0])\n    \u003e\u003e\u003e sorted(b)\n    ['a', 'b', 'c']\n    \u003e\u003e\u003e list(b['a'])\n    ['a1', 'a2']\n\nNote that once consumed, you can't iterate over the contents\nof a group again.\n\n\n.. _Iter.unzip:\n\n\n``Iter.unzip(self)``\n====================\nDocstring TODO\n\n\n.. _Iter.grouper:\n\n\n``Iter.grouper(self, n: int, fillvalue=None) -\u003e \"Iter\"``\n========================================================\nDocstring TODO\n\n\n.. _Iter.partition:\n\n\n``Iter.partition(self, pred) -\u003e \"Iter\"``\n========================================\nDocstring TODO\n\n\n.. _Iter.spy:\n\n\n``Iter.spy(self, n=1) -\u003e \"Tuple[Iter, Iter]\"``\n==============================================\n\nReplacement for the more-itertools ``spy`` function.  This version returns\ninstances of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.spy \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.spy\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e head, iterable = Iter('abcdefg').spy()\n    \u003e\u003e\u003e head.collect()\n    ['a']\n    \u003e\u003e\u003e iterable.collect()\n    ['a', 'b', 'c', 'd', 'e', 'f', 'g']\n    \u003e\u003e\u003e (head,), iterable = Iter('abcdefg').spy()\n    \u003e\u003e\u003e head\n    'a'\n    \u003e\u003e\u003e (first, second), iterable = Iter('abcdefg').spy(2)\n    \u003e\u003e\u003e first\n    'a'\n    \u003e\u003e\u003e second\n    'b'\n\n\n\n.. _Iter.peekable:\n\n\n``Iter.peekable(self) -\u003e \"more_itertools.peekable\"``\n====================================================\n\nReference: `more_itertools.peekable \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.peekable\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e p = Iter(['a', 'b']).peekable()\n    \u003e\u003e\u003e p.peek()\n    'a'\n    \u003e\u003e\u003e next(p)\n    'a'\n\nThe peekable can be used to inspect what will be coming up.\nBut if you then want to resume iterator chaining, pass the\npeekable back into an Iter_ instance.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e p = Iter(range(10)).peekable()\n    \u003e\u003e\u003e p.peek()\n    0\n    \u003e\u003e\u003e Iter(p).take(3).collect()\n    [0, 1, 2]\n\nA peekable is not an Iter_ instance so it doesn't provide\nthe iterator chaining methods. But if you want to get into\nchaining, use the ``iter()`` method.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e p = Iter(range(5)).peekable()\n    \u003e\u003e\u003e p.peek()\n    0\n    \u003e\u003e\u003e p[1]\n    1\n    \u003e\u003e\u003e p.iter().take(3).collect()\n    [0, 1, 2]\n\nPeekables can be prepended. But then you usually want to go\nright back to iterator chaining. Thus, the ``prepend`` method\n(on the returned ``peekable`` instance) returns an Iter_ instance.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e p = Iter(range(3)).peekable()\n    \u003e\u003e\u003e p.peek()\n    0\n    \u003e\u003e\u003e p.prepend('a', 'b').take(4).collect()\n    ['a', 'b', 0, 1]\n\n\n\n.. _Iter.seekable:\n\n\n``Iter.seekable(self, maxlen=None) -\u003e \"IterSeekable[T]\"``\n=========================================================\n\nReference: `more_itertools.seekable \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.seekable\u003e`_\n\nAllow for seeking forward and backward.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e it = count().map(str).seekable()\n    \u003e\u003e\u003e next(it), next(it), next(it)\n    ('0', '1', '2')\n    \u003e\u003e\u003e it.seek(0).take(3).collect(tuple)\n    ('0', '1', '2')\n\nSeeking forward:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e it = range(20).map(str).seekable()\n    \u003e\u003e\u003e it.seek(10).next()\n    '10'\n    \u003e\u003e\u003e it.seek(20).collect()\n    []\n    \u003e\u003e\u003e it.seek(0).next()\n    '0'\n\nCall `relative_seek()` to seek relative to the current position:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e it = range(20).map(str).seekable()\n    \u003e\u003e\u003e it.take(3).collect(tuple)\n    ('0', '1', '2')\n    \u003e\u003e\u003e it.relative_seek(2).next()\n    '5'\n    \u003e\u003e\u003e it.relative_seek(-3).next()\n    '3'\n    \u003e\u003e\u003e it.relative_seek(-3).next()\n    '1'\n\nCall peek() to look ahead one item without advancing the iterator:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e it = Iter('1234').seekable()\n    \u003e\u003e\u003e it.peek()\n    '1'\n    \u003e\u003e\u003e it.collect()\n    ['1', '2', '3', '4']\n    \u003e\u003e\u003e it.peek(default='empty')\n    'empty'\n\nBefore the iterator is at its end, calling bool() on it will\nreturn ``True``. After it will return ``False``.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e it = Iter('5678').seekable()\n    \u003e\u003e\u003e it.bool()\n    True\n    \u003e\u003e\u003e it.collect()\n    ['5', '6', '7', '8']\n    \u003e\u003e\u003e it.bool()\n    False\n\nUse ``maxlen`` to limit the size of the internal cache used\nfor seeking. This is useful to prevent memory issues when\nseeking through a very large iterator.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e it = count().map(str).seekable(maxlen=2)\n    \u003e\u003e\u003e it.take(4).collect(tuple)\n    ('0', '1', '2', '3')\n    \u003e\u003e\u003e it.seek(0).take(4).collect(tuple)\n    ('2', '3', '4', '5')\n\n\n\n.. _Iter.windowed:\n\n\n``Iter.windowed(self, n, fillvalue=None, step=1) -\u003e \"Iter\"``\n============================================================\n\nReplacement for the more-itertools ``windowed`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.windowed \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3, 4, 5]).windowed(3).collect()\n    [(1, 2, 3), (2, 3, 4), (3, 4, 5)]\n    \u003e\u003e\u003e Iter([1, 2, 3]).windowed(4).collect()\n    [(1, 2, 3, None)]\n    \u003e\u003e\u003e Iter([1, 2, 3, 4, 5, 6]).windowed(3, fillvalue='!', step=2).collect()\n    [(1, 2, 3), (3, 4, 5), (5, 6, '!')]\n\n\n\n.. _Iter.substrings:\n\n\n``Iter.substrings(self) -\u003e \"Iter\"``\n===================================\n\nReplacement for the more-itertools ``substrings`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.substrings \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('more').substrings().map(\"\".join).collect()\n    ['m', 'o', 'r', 'e', 'mo', 'or', 're', 'mor', 'ore', 'more']\n    \u003e\u003e\u003e Iter([0, 1, 2]).substrings().collect()\n    [(0,), (1,), (2,), (0, 1), (1, 2), (0, 1, 2)]\n\n\n\n.. _Iter.substrings_indexes:\n\n\n``Iter.substrings_indexes(self, reverse=False) -\u003e \"Iter\"``\n==========================================================\n\n|warning| This function is not lazy and will consume the iterable fully before returning\nanother iterable.\n\nReplacement for the more-itertools ``substrings_indexes`` function.  This version returns\nan instance of Iter_ to allow further iterable chaining.\n\nReference: `more_itertools.substrings_indexes \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings_indexes\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e (\n    ...     Iter('more')\n    ...     .substrings_indexes()\n    ...     .starmap(lambda sub, start, end: (\"\".join(sub), start, end))\n    ...     .side_effect(print)\n    ...     .consume()\n    ... )\n    ('m', 0, 1)\n    ('o', 1, 2)\n    ('r', 2, 3)\n    ('e', 3, 4)\n    ('mo', 0, 2)\n    ('or', 1, 3)\n    ('re', 2, 4)\n    ('mor', 0, 3)\n    ('ore', 1, 4)\n    ('more', 0, 4)\n\n\n\n.. _Iter.stagger:\n\n\n``Iter.stagger(self, offsets=(-1, 0, 1), longest=False, fillvalue=None)``\n=========================================================================\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([0, 1, 2, 3]).stagger().collect()\n    [(None, 0, 1), (0, 1, 2), (1, 2, 3)]\n    \u003e\u003e\u003e Iter(range(8)).stagger(offsets=(0, 2, 4)).collect()\n    [(0, 2, 4), (1, 3, 5), (2, 4, 6), (3, 5, 7)]\n    \u003e\u003e\u003e Iter([0, 1, 2, 3]).stagger(longest=True).collect()\n    [(None, 0, 1), (0, 1, 2), (1, 2, 3), (2, 3, None), (3, None, None)]\n\n\n\n.. _Iter.pairwise:\n\n\n``Iter.pairwise(self)``\n=======================\n\nReference `more_itertools.pairwise \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.pairwise\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.count().pairwise().take(4).collect()\n    [(0, 1), (1, 2), (2, 3), (3, 4)]\n\n\n.. _Iter.count_cycle:\n\n\n``Iter.count_cycle(self, n=None) -\u003e \"Iter\"``\n============================================\n\nReference: `more_itertools.count_cycle \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.count_cycle\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('AB').count_cycle(3).collect()\n    [(0, 'A'), (0, 'B'), (1, 'A'), (1, 'B'), (2, 'A'), (2, 'B')]\n\n\n\n.. _Iter.intersperse:\n\n\n``Iter.intersperse(self, e, n=1) -\u003e \"Iter\"``\n============================================\n\nReference: `more_itertools.intersperse \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.intersperse\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3, 4, 5]).intersperse('!').collect()\n    [1, '!', 2, '!', 3, '!', 4, '!', 5]\n\n    \u003e\u003e\u003e Iter([1, 2, 3, 4, 5]).intersperse(None, n=2).collect()\n    [1, 2, None, 3, 4, None, 5]\n\n\n\n.. _Iter.padded:\n\n\n``Iter.padded(self, fillvalue: Optional[C] = None, n: Optional[int] = None, next_multiple: bool = False, ) -\u003e \"Iter[Union[T, C]]\"``\n===================================================================================================================================\n\nReference: `more_itertools.padded \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.padded\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3]).padded('?', 5).collect()\n    [1, 2, 3, '?', '?']\n\n    \u003e\u003e\u003e Iter([1, 2, 3, 4]).padded(n=3, next_multiple=True).collect()\n    [1, 2, 3, 4, None, None]\n\n\n\n.. _Iter.repeat_last:\n\n\n``Iter.repeat_last(self, default=None) -\u003e \"Iter[T]\"``\n=====================================================\n\nReference: `more_itertools.repeat_last \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeat_last\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(3)).repeat_last().islice(5).collect()\n    [0, 1, 2, 2, 2]\n\n    \u003e\u003e\u003e Iter(range(0)).repeat_last(42).islice(5).collect()\n    [42, 42, 42, 42, 42]\n\n\n\n.. _Iter.adjacent:\n\n\n``Iter.adjacent(self, pred, distance=1) -\u003e \"Iter[Tuple[bool, T]]\"``\n===================================================================\n\nReference: `more_itertools.adjacent \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.adjacent\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(6)).adjacent(lambda x: x == 3).collect()\n    [(False, 0), (False, 1), (True, 2), (True, 3), (True, 4), (False, 5)]\n\n    \u003e\u003e\u003e Iter(range(6)).adjacent(lambda x: x == 3, distance=2).collect()\n    [(False, 0), (True, 1), (True, 2), (True, 3), (True, 4), (True, 5)]\n\n\n\n\n.. _Iter.groupby_transform:\n\n\n``Iter.groupby_transform(self, keyfunc: Optional[Callable[..., K]] = None, valuefunc: Optional[Callable[..., V]] = None, ) -\u003e \"Iter[Tuple[K, Iterable[V]]]\"``\n=============================================================================================================================================================\n\nReference: `more_itertools.groupby_transform \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.groupby_transform\u003e`_\n\nThis example has been modified somewhat from the original. We're using\n``starmap`` here to \"unzip\" the tuples produced by the group\ntransform.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = 'AaaABbBCcA'\n    \u003e\u003e\u003e keyfunc = lambda x: x.upper()\n    \u003e\u003e\u003e valuefunc = lambda x: x.lower()\n    \u003e\u003e\u003e (\n    ...    Iter(iterable)\n    ...        .groupby_transform(keyfunc, valuefunc)\n    ...        .starmap(lambda k, g: (k, ''.join(g)))\n    ...        .collect()\n    ... )\n    [('A', 'aaaa'), ('B', 'bbb'), ('C', 'cc'), ('A', 'a')]\n\n    \u003e\u003e\u003e from operator import itemgetter\n    \u003e\u003e\u003e keys = [0, 0, 1, 1, 1, 2, 2, 2, 3]\n    \u003e\u003e\u003e values = 'abcdefghi'\n    \u003e\u003e\u003e iterable = zip(keys, values)\n    \u003e\u003e\u003e (\n    ...     Iter(iterable)\n    ...        .groupby_transform(itemgetter(0), itemgetter(1))\n    ...        .starmap(lambda k, g: (k, ''.join(g)))\n    ...        .collect()\n    ... )\n    [(0, 'ab'), (1, 'cde'), (2, 'fgh'), (3, 'i')]\n\n\n\n.. _Iter.padnone:\n\n\n``Iter.padnone(self) -\u003e \"Iter[Union[T, None]]\"``\n================================================\n\nReference: `more_itertools.padnone \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.padnone\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(3)).padnone().take(5).collect()\n    [0, 1, 2, None, None]\n\n\n\n.. _Iter.ncycles:\n\n\n``Iter.ncycles(self, n) -\u003e Self``\n=================================\n\nReference: `more_itertools.ncycles \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ncycles\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(['a', 'b']).ncycles(3).collect()\n    ['a', 'b', 'a', 'b', 'a', 'b']\n\n\n\n.. _Iter.collapse:\n\n\n``Iter.collapse(self, base_type=None, levels=None) -\u003e Self``\n============================================================\n\nReference: `more_itertools.collapse \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = [(1, 2), ([3, 4], [[5], [6]])]\n    \u003e\u003e\u003e Iter(iterable).collapse().collect()\n    [1, 2, 3, 4, 5, 6]\n\n    \u003e\u003e\u003e iterable = ['ab', ('cd', 'ef'), ['gh', 'ij']]\n    \u003e\u003e\u003e Iter(iterable).collapse(base_type=tuple).collect()\n    ['ab', ('cd', 'ef'), 'gh', 'ij']\n\n    \u003e\u003e\u003e iterable = [('a', ['b']), ('c', ['d'])]\n    \u003e\u003e\u003e Iter(iterable).collapse().collect() # Fully flattened\n    ['a', 'b', 'c', 'd']\n    \u003e\u003e\u003e Iter(iterable).collapse(levels=1).collect() # Only one level flattened\n    ['a', ['b'], 'c', ['d']]\n\n\n\n.. _Iter.sort_together:\n\n\n``@class_or_instancemethod Iter.sort_together(self_or_cls, iterables, key_list=(0,), reverse=False)``\n=====================================================================================================\n\nReference: `more_itertools.sort_together \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sort_together\u003e`_\n\nThis can be called either as an instance method or a class method.\nThe classmethod form is more convenient if all the iterables are\nalready available. The instancemethod form is more convenient if\none of the iterables already goes through some transformation.\n\nHere are examples from the classmethod form, which mirror the\nexamples in the more-itertools_ documentation:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterables = [(4, 3, 2, 1), ('a', 'b', 'c', 'd')]\n    \u003e\u003e\u003e Iter.sort_together(iterables).collect()\n    [(1, 2, 3, 4), ('d', 'c', 'b', 'a')]\n\n    \u003e\u003e\u003e iterables = [(3, 1, 2), (0, 1, 0), ('c', 'b', 'a')]\n    \u003e\u003e\u003e Iter.sort_together(iterables, key_list=(1, 2)).collect()\n    [(2, 3, 1), (0, 0, 1), ('a', 'c', 'b')]\n\n    \u003e\u003e\u003e Iter.sort_together([(1, 2, 3), ('c', 'b', 'a')], reverse=True).collect()\n    [(3, 2, 1), ('a', 'b', 'c')]\n\nHere is an examples using the instancemethod form:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterables = [('a', 'b', 'c', 'd')]\n    \u003e\u003e\u003e Iter([4, 3, 2, 1]).sort_together(iterables).collect()\n    [(1, 2, 3, 4), ('d', 'c', 'b', 'a')]\n\n\n\n.. _Iter.interleave:\n\n\n``@class_or_instancemethod Iter.interleave(self_or_cls, *iterables) -\u003e Self``\n=============================================================================\n\nReference: `more_itertools.interleave \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave\u003e`_\n\nClassmethod form:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.interleave([1, 2, 3], [4, 5], [6, 7, 8]).collect()\n    [1, 4, 6, 2, 5, 7]\n\nInstancemethod form:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3]).interleave([4, 5], [6, 7, 8]).collect()\n    [1, 4, 6, 2, 5, 7]\n\n\n\n.. _Iter.interleave_longest:\n\n\n``@class_or_instancemethod Iter.interleave_longest(self_or_cls, *iterables) -\u003e Self``\n=====================================================================================\n\nReference: `more_itertools.interleave_longest \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_longest\u003e`_\n\nClassmethod form:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.interleave_longest([1, 2, 3], [4, 5], [6, 7, 8]).collect()\n    [1, 4, 6, 2, 5, 7, 3, 8]\n\nInstancemethod form:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3]).interleave_longest([4, 5], [6, 7, 8]).collect()\n    [1, 4, 6, 2, 5, 7, 3, 8]\n\n\n\n.. _Iter.zip_offset:\n\n\n``@classmethod Iter.zip_offset(cls, *iterables, offsets, longest=False, fillvalue=None) -\u003e Self``\n=================================================================================================\n\nReference: `more_itertools.zip_offset \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_offset\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.zip_offset('0123', 'abcdef', offsets=(0, 1)).collect()\n    [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e')]\n\n    \u003e\u003e\u003e Iter.zip_offset('0123', 'abcdef', offsets=(0, 1), longest=True).collect()\n    [('0', 'b'), ('1', 'c'), ('2', 'd'), ('3', 'e'), (None, 'f')]\n\n\n.. _Iter.dotproduct:\n\n\n``Iter.dotproduct(self, vec2: Iterable)``\n=========================================\n\nReference: `more_itertools.dotproduct \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([10, 10]).dotproduct([20, 20])\n    400\n\n\n.. _Iter.flatten:\n\n\n``Iter.flatten(self) -\u003e Self``\n==============================\n\nReference: `more_itertools.flatten \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.flatten\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([[0, 1], [2, 3]]).flatten().collect()\n    [0, 1, 2, 3]\n\n\n\n.. _Iter.roundrobin:\n\n\n``@class_or_instancemethod Iter.roundrobin(self_or_cls: Union[Type[T], T], *iterables: C) -\u003e \"Iter[Union[T, C]]\"``\n==================================================================================================================\n\nReference: `more_itertools.roundrobin \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.roundrobin\u003e`_\n\nClassmethod form:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.roundrobin('ABC', 'D', 'EF').collect()\n    ['A', 'D', 'E', 'B', 'F', 'C']\n\nInstancemethod form:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('ABC').roundrobin('D', 'EF').collect()\n    ['A', 'D', 'E', 'B', 'F', 'C']\n\n\n\n.. _Iter.prepend:\n\n\n``Iter.prepend(self, value: C) -\u003e \"Iter[Union[T, C]]\"``\n=======================================================\n\nReference: `more_itertools.prepend \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.prepend\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e value = '0'\n    \u003e\u003e\u003e iterator = ['1', '2', '3']\n    \u003e\u003e\u003e Iter(iterator).prepend(value).collect()\n    ['0', '1', '2', '3']\n\n\n\n.. _Iter.ilen:\n\n\n|sink| ``Iter.ilen(self) -\u003e \"int\"``\n===================================\n\n\n\nReference: `more_itertools.ilen \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ilen\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(x for x in range(1000000) if x % 3 == 0).ilen()\n    333334\n\n\n\n.. _Iter.unique_to_each:\n\n\n``Iter.unique_to_each(self) -\u003e Self``\n=====================================\n\nReference: `more_itertools.unique_to_each \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_to_each\u003e`_\n\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([{'A', 'B'}, {'B', 'C'}, {'B', 'D'}]).unique_to_each().collect()\n    [['A'], ['C'], ['D']]\n\n    \u003e\u003e\u003e Iter([\"mississippi\", \"missouri\"]).unique_to_each().collect()\n    [['p', 'p'], ['o', 'u', 'r']]\n\nNote that this will internally construct the full list of the uniques for each group.\n\n\n\n.. _Iter.sample:\n\n\n``Iter.sample(self, k=1, weights=None) -\u003e \"Iter\"``\n==================================================\n\nReference: `more_itertools.sample \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sample\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = range(100)\n    \u003e\u003e\u003e Iter(iterable).sample(5).collect()                  \n    [81, 60, 96, 16, 4]\n\n    \u003e\u003e\u003e iterable = range(100)\n    \u003e\u003e\u003e weights = (i * i + 1 for i in range(100))\n    \u003e\u003e\u003e Iter(iterable).sample(5, weights=weights)                  \n    [79, 67, 74, 66, 78]\n\n    \u003e\u003e\u003e data = \"abcdefgh\"\n    \u003e\u003e\u003e weights = range(1, len(data) + 1)\n    \u003e\u003e\u003e Iter(data).sample(k=len(data), weights=weights)                  \n    ['c', 'a', 'b', 'e', 'g', 'd', 'h', 'f']\n\n\n    \u003e\u003e\u003e # This one just to let the doctest run\n    \u003e\u003e\u003e iterable = range(100)\n    \u003e\u003e\u003e Iter(iterable).sample(5).map(lambda x: 0 \u003c= x \u003c 100).all()\n    True\n\n\n\n.. _Iter.consecutive_groups:\n\n\n``Iter.consecutive_groups(self, ordering=lambda x: x)``\n=======================================================\n\nReference: `more_itertools.consecutive_groups \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consecutive_groups\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = [1, 10, 11, 12, 20, 30, 31, 32, 33, 40]\n    \u003e\u003e\u003e Iter(iterable).consecutive_groups().map(lambda g: list(g)).print('{v}').consume()\n    [1]\n    [10, 11, 12]\n    [20]\n    [30, 31, 32, 33]\n    [40]\n\n\n\n.. _Iter.run_length_encode:\n\n\n``Iter.run_length_encode(self) -\u003e \"Iter[Tuple[T, int]]\"``\n=========================================================\n\nReference: `more_itertools.run_length \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.run_length\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e uncompressed = 'abbcccdddd'\n    \u003e\u003e\u003e Iter(uncompressed).run_length_encode().collect()\n    [('a', 1), ('b', 2), ('c', 3), ('d', 4)]\n\n\n\n.. _Iter.run_length_decode:\n\n\n``Iter.run_length_decode(self) -\u003e \"Iter\"``\n==========================================\n\nReference: `more_itertools.run_length \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.run_length\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e compressed = [('a', 1), ('b', 2), ('c', 3), ('d', 4)]\n    \u003e\u003e\u003e Iter(compressed).run_length_decode().collect()\n    ['a', 'b', 'b', 'c', 'c', 'c', 'd', 'd', 'd', 'd']\n\n\n\n.. _Iter.map_reduce:\n\n\n``Iter.map_reduce(self, keyfunc, valuefunc=None, reducefunc=None) -\u003e \"Dict\"``\n=============================================================================\n\nReference: `more_itertools.map_reduce \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_reduce\u003e`_\n\nThis interface mirrors what more-itertools_ does in that it returns\na dict. See ``map_reduce_it()`` for a slightly-modified interface\nthat returns the dict items as another iterator.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e keyfunc = lambda x: x.upper()\n    \u003e\u003e\u003e d = Iter('abbccc').map_reduce(keyfunc)\n    \u003e\u003e\u003e sorted(d.items())\n    [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])]\n\n    \u003e\u003e\u003e keyfunc = lambda x: x.upper()\n    \u003e\u003e\u003e valuefunc = lambda x: 1\n    \u003e\u003e\u003e d = Iter('abbccc').map_reduce(keyfunc, valuefunc)\n    \u003e\u003e\u003e sorted(d.items())\n    [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])]\n\n    \u003e\u003e\u003e keyfunc = lambda x: x.upper()\n    \u003e\u003e\u003e valuefunc = lambda x: 1\n    \u003e\u003e\u003e reducefunc = sum\n    \u003e\u003e\u003e d = Iter('abbccc').map_reduce(keyfunc, valuefunc, reducefunc)\n    \u003e\u003e\u003e sorted(d.items())\n    [('A', 1), ('B', 2), ('C', 3)]\n\nNote the warning given in the more-itertools_ docs about how\nlists are created before the reduce step. This means you always want\nto filter *before* applying map_reduce, not after.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e all_items = _range(30)\n    \u003e\u003e\u003e keyfunc = lambda x: x % 2  # Evens map to 0; odds to 1\n    \u003e\u003e\u003e categories = Iter(all_items).filter(lambda x: 10\u003c=x\u003c=20).map_reduce(keyfunc=keyfunc)\n    \u003e\u003e\u003e sorted(categories.items())\n    [(0, [10, 12, 14, 16, 18, 20]), (1, [11, 13, 15, 17, 19])]\n    \u003e\u003e\u003e summaries = Iter(all_items).filter(lambda x: 10\u003c=x\u003c=20).map_reduce(keyfunc=keyfunc, reducefunc=sum)\n    \u003e\u003e\u003e sorted(summaries.items())\n    [(0, 90), (1, 75)]\n\n\n\n.. _Iter.map_reduce_it:\n\n\n``Iter.map_reduce_it(self, keyfunc: Callable[..., K], valuefunc: Optional[Callable[..., V]] = None, reducefunc: Optional[Callable[..., R]] = None, ) -\u003e \"Iter[Tuple[K, R]]\"``\n=============================================================================================================================================================================\n\nReference: `more_itertools.map_reduce \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_reduce\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e keyfunc = lambda x: x.upper()\n    \u003e\u003e\u003e Iter('abbccc').map_reduce_it(keyfunc).collect()\n    [('A', ['a']), ('B', ['b', 'b']), ('C', ['c', 'c', 'c'])]\n\n    \u003e\u003e\u003e keyfunc = lambda x: x.upper()\n    \u003e\u003e\u003e valuefunc = lambda x: 1\n    \u003e\u003e\u003e Iter('abbccc').map_reduce_it(keyfunc, valuefunc).collect()\n    [('A', [1]), ('B', [1, 1]), ('C', [1, 1, 1])]\n\n    \u003e\u003e\u003e keyfunc = lambda x: x.upper()\n    \u003e\u003e\u003e valuefunc = lambda x: 1\n    \u003e\u003e\u003e reducefunc = sum\n    \u003e\u003e\u003e Iter('abbccc').map_reduce_it(keyfunc, valuefunc, reducefunc).collect()\n    [('A', 1), ('B', 2), ('C', 3)]\n\n\n\n.. _Iter.exactly_n:\n\n\n|sink| ``Iter.exactly_n(self, n, predicate=bool) -\u003e \"bool\"``\n============================================================\n\n\n\nReference: `more_itertools.exactly_n \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.exactly_n\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([True, True, False]).exactly_n(2)\n    True\n\n\n\n.. _Iter.is_sorted:\n\n\n|sink| ``Iter.is_sorted(self, key=None, reverse=False) -\u003e \"bool\"``\n==================================================================\n\n\n\nReference: `more_itertools.is_sorted \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.is_sorted\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3]).is_sorted()\n    True\n\n\n\n.. _Iter.all_unique:\n\n\n|sink| ``Iter.all_unique(self, key=None) -\u003e \"bool\"``\n====================================================\n\n\n\nReference: `more_itertools.all_unique \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_unique\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3]).all_unique()\n    True\n\n\n\n.. _Iter.minmax:\n\n\n|sink| ``Iter.minmax(self, key: Optional[Callable[[T], Any]] = None, default: Optional[tuple[T, T]] = None) -\u003e \"Tuple[T, T]\"``\n==============================================================================================================================\n\n\n\nReference: `more_itertools.minmax \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.minmax\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3]).minmax()\n    (1, 3)\n    \u003e\u003e\u003e Iter([5, 30]).minmax(key=str)\n    (30, 5)\n    \u003e\u003e\u003e Iter([]).minmax(default=(0, 0))\n    (0, 0)\n\n\n\n.. _Iter.all_equal:\n\n\n|sink| ``Iter.all_equal(self, key: Optional[Callable[[T], Any]] = None) -\u003e bool``\n=================================================================================\n\n\n\nReference: `more_itertools.all_equal \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_equal\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 1, 1]).all_equal()\n    True\n    \u003e\u003e\u003e Iter([1, 2, 1]).all_equal()\n    False\n    \u003e\u003e\u003e Iter(\"aaaa\").all_equal()\n    True\n\n\n\n.. _Iter.first_true:\n\n\n|sink| ``Iter.first_true(self, default=None, pred=None) -\u003e Optional[T]``\n========================================================================\n\n\n\nReference: `more_itertools.first_true \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first_true\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([False, False, True, False]).first_true()\n    True\n    \u003e\u003e\u003e Iter(range(10)).first_true()\n    1\n    \u003e\u003e\u003e Iter(range(10)).first_true(pred=lambda x: x \u003e 5)\n    6\n    \u003e\u003e\u003e Iter(range(10)).first_true(default=\"missing\", pred=lambda x: x \u003e 9)\n    'missing'\n\n\n\n.. _Iter.quantify:\n\n\n|sink| ``Iter.quantify(self)``\n==============================\n\n\n\nReference: `more_itertools.quantify \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.quantify\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([True, False, True, False, True]).quantify()\n    3\n\n\n\n.. _Iter.islice_extended:\n\n\n``Iter.islice_extended(self, *args) -\u003e \"Iter\"``\n===============================================\n\nReference: `more_itertools.islice_extended \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.i","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcjrh%2Fexcitertools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcjrh%2Fexcitertools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcjrh%2Fexcitertools/lists"}