{"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","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.islice_extended\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abcdefgh').islice_extended(-4, -1).collect()\n    ['e', 'f', 'g']\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.count().islice_extended(110, 99, -2).collect()\n    [110, 108, 106, 104, 102, 100]\n\n\n\n.. _Iter.first:\n\n\n``Iter.first(self, default: \"V\" = None) -\u003e \"T | V\"``\n====================================================\n\nReference: `more_itertools.first \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3]).first()\n    1\n    \u003e\u003e\u003e Iter([]).first(default='missing')\n    'missing'\n\n\n\n.. _Iter.last:\n\n\n``Iter.last(self, default: \"V\" = None) -\u003e \"T | V\"``\n===================================================\n\nReference: `more_itertools.last \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.last\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3]).last()\n    3\n    \u003e\u003e\u003e Iter([]).last(default='missing')\n    'missing'\n\n\n.. _Iter.one:\n\n\n``Iter.one(self, too_short=ValueError, too_long=ValueError) -\u003e \"T\"``\n====================================================================\n\nReturn the first item from the iterable, raising an exception if there\nis not exactly one item.\n\nNote that one() attempts to advance iterable twice to ensure there is\nonly one item. See Iter.spy_ or Iter.peekable_ to check iterable\ncontents less destructively.\n\nReference: `more_itertools.one \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.one\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([]).one()\n    Traceback (most recent call last):\n        ...\n    ValueError: ...\n    \u003e\u003e\u003e Iter([42]).one()\n    42\n\n\n\n.. _Iter.only:\n\n\n``Iter.only(self, default=None, too_long=ValueError) -\u003e \"T\"``\n=============================================================\n\nReference: `more_itertools.one \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.one\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([]).only(default='missing')\n    'missing'\n    \u003e\u003e\u003e Iter([42]).only(default='missing')\n    42\n    \u003e\u003e\u003e Iter([1, 2]).only()\n    Traceback (most recent call last):\n        ...\n    ValueError: ...\n\n\n\n.. _Iter.strictly_n:\n\n\n``Iter.strictly_n(self, n, too_short=None, too_long=None) -\u003e \"Self\"``\n=====================================================================\n\nMake sure you understand how the underlying function works. Check\nthe more-itertools_ documentation for more information.\n\nReference: `more_itertools.strictly_n \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.strictly_n\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3]).strictly_n(3).collect()\n    [1, 2, 3]\n    \u003e\u003e\u003e Iter([1, 2, 3]).strictly_n(2).collect()\n    Traceback (most recent call last):\n        ...\n    ValueError: ...\n    \u003e\u003e\u003e Iter([1, 2, 3]).strictly_n(4).collect()\n    Traceback (most recent call last):\n        ...\n    ValueError: ...\n\n\n\n.. _Iter.strip:\n\n\n``Iter.strip(self, pred) -\u003e \"Iter[T]\"``\n=======================================\n\nReference: `more_itertools.strip \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.strip\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = (None, False, None, 1, 2, None, 3, False, None)\n    \u003e\u003e\u003e pred = lambda x: x in {None, False, ''}\n    \u003e\u003e\u003e Iter(iterable).strip(pred).collect()\n    [1, 2, None, 3]\n\n\n\n.. _Iter.lstrip:\n\n\n``Iter.lstrip(self, pred) -\u003e \"Iter[T]\"``\n========================================\n\nReference: `more_itertools.lstrip \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.lstrip\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = (None, False, None, 1, 2, None, 3, False, None)\n    \u003e\u003e\u003e pred = lambda x: x in {None, False, ''}\n    \u003e\u003e\u003e Iter(iterable).lstrip(pred).collect()\n    [1, 2, None, 3, False, None]\n\n\n\n.. _Iter.rstrip:\n\n\n``Iter.rstrip(self, pred) -\u003e \"Iter[T]\"``\n========================================\n\nReference: `more_itertools.rstrip \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rstrip\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = (None, False, None, 1, 2, None, 3, False, None)\n    \u003e\u003e\u003e pred = lambda x: x in {None, False, ''}\n    \u003e\u003e\u003e Iter(iterable).rstrip(pred).collect()\n    [None, False, None, 1, 2, None, 3]\n\n\n\n.. _Iter.filter_except:\n\n\n``Iter.filter_except(self, validator, *exceptions) -\u003e \"Iter[T]\"``\n=================================================================\n\nReference: `more_itertools.filter_except \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.filter_except\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = ['1', '2', 'three', '4', None]\n    \u003e\u003e\u003e Iter(iterable).filter_except(int, ValueError, TypeError).collect()\n    ['1', '2', '4']\n\n\n\n.. _Iter.map_except:\n\n\n``Iter.map_except(self, function, *exceptions) -\u003e \"Iter\"``\n==========================================================\n\nReference: `more_itertools.map_except \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_except\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = ['1', '2', 'three', '4', None]\n    \u003e\u003e\u003e Iter(iterable).map_except(int, ValueError, TypeError).collect()\n    [1, 2, 4]\n\n\n\n.. _Iter.nth_or_last:\n\n\n``Iter.nth_or_last(self, n, default=_marker) -\u003e \"T\"``\n=====================================================\n\nReference: `more_itertools.nth_or_last \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_or_last\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([0, 1, 2, 3]).nth_or_last(2)\n    2\n    \u003e\u003e\u003e Iter([0, 1]).nth_or_last(2)\n    1\n    \u003e\u003e\u003e Iter([]).nth_or_last(0, 'some default')\n    'some default'\n\n\n\n.. _Iter.nth:\n\n\n``Iter.nth(self, n, default=None)``\n===================================\n\nReference: `more_itertools.nth \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth\u003e`_\n\n\n.. _Iter.take:\n\n\n``Iter.take(self, n: int) -\u003e \"Iter\"``\n=====================================\n\nReference: `more_itertools.take \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.take\u003e`_\n\n\n.. _Iter.tail:\n\n\n``Iter.tail(self, n) -\u003e \"Iter[T]\"``\n===================================\n\nReference: `more_itertools.tail \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tail\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('ABCDEFG').tail(3).collect()\n    ['E', 'F', 'G']\n\n\n\n.. _Iter.unique_everseen:\n\n\n``Iter.unique_everseen(self, key=None) -\u003e \"Iter[T]\"``\n=====================================================\n\nReference: `more_itertools.unique_everseen \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_everseen\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('AAAABBBCCDAABBB').unique_everseen().collect()\n    ['A', 'B', 'C', 'D']\n    \u003e\u003e\u003e Iter('ABBCcAD').unique_everseen(key=str.lower).collect()\n    ['A', 'B', 'C', 'D']\n\nBe sure to read the *more-itertools* docs whne using unhashable\nitems.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = ([1, 2], [2, 3], [1, 2])\n    \u003e\u003e\u003e Iter(iterable).unique_everseen().collect()  # Slow\n    [[1, 2], [2, 3]]\n    \u003e\u003e\u003e Iter(iterable).unique_everseen(key=tuple).collect()  # Faster\n    [[1, 2], [2, 3]]\n\n\n\n.. _Iter.unique_justseen:\n\n\n``Iter.unique_justseen(self, key=None) -\u003e \"Iter[T]\"``\n=====================================================\n\nReference: `more_itertools.unique_justseen \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_justseen\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('AAAABBBCCDAABBB').unique_justseen().collect()\n    ['A', 'B', 'C', 'D', 'A', 'B']\n    \u003e\u003e\u003e Iter('ABBCcAD').unique_justseen(key=str.lower).collect()\n    ['A', 'B', 'C', 'A', 'D']\n\n\n\n.. _Iter.distinct_permutations:\n\n\n``Iter.distinct_permutations(self)``\n====================================\n\nReference: `more_itertools.distinct_permutations \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_permutations\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 0, 1]).distinct_permutations().sorted().collect()\n    [(0, 1, 1), (1, 0, 1), (1, 1, 0)]\n\n\n\n.. _Iter.distinct_combinations:\n\n\n``Iter.distinct_combinations(self, r) -\u003e \"Iter[T]\"``\n====================================================\n\nReference: `more_itertools.distinct_combinations \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_combinations\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([0, 0, 1]).distinct_combinations(2).collect()\n    [(0, 0), (0, 1)]\n\n\n\n.. _Iter.circular_shifts:\n\n\n``Iter.circular_shifts(self) -\u003e \"Iter[T]\"``\n===========================================\n\nReference: `more_itertools.circular_shifts \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.circular_shifts\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(4)).circular_shifts().collect()\n    [(0, 1, 2, 3), (1, 2, 3, 0), (2, 3, 0, 1), (3, 0, 1, 2)]\n\n\n\n.. _Iter.partitions:\n\n\n``Iter.partitions(self) -\u003e \"Iter[T]\"``\n======================================\n\nReference: `more_itertools.partitions \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partitions\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').partitions().collect()\n    [[['a', 'b', 'c']], [['a'], ['b', 'c']], [['a', 'b'], ['c']], [['a'], ['b'], ['c']]]\n    \u003e\u003e\u003e Iter('abc').partitions().print('{v}').consume()\n    [['a', 'b', 'c']]\n    [['a'], ['b', 'c']]\n    [['a', 'b'], ['c']]\n    [['a'], ['b'], ['c']]\n    \u003e\u003e\u003e Iter('abc').partitions().map(lambda v: [''.join(p) for p in v]).print('{v}').consume()\n    ['abc']\n    ['a', 'bc']\n    ['ab', 'c']\n    ['a', 'b', 'c']\n\n\n\n.. _Iter.set_partitions:\n\n\n``Iter.set_partitions(self, k=None) -\u003e \"Iter[T]\"``\n==================================================\n\nReference: `more_itertools.set_partitions \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.set_partitions\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').set_partitions(2).collect()\n    [[['a'], ['b', 'c']], [['a', 'b'], ['c']], [['b'], ['a', 'c']]]\n\n\n\n.. _Iter.powerset:\n\n\n``Iter.powerset(self)``\n=======================\n\nReference: `more_itertools.powerset \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.powerset\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([1, 2, 3]).powerset().collect()\n    [(), (1,), (2,), (3,), (1, 2), (1, 3), (2, 3), (1, 2, 3)]\n\n\n\n.. _Iter.random_product:\n\n\n``@class_or_instancemethod Iter.random_product(self_or_cls, *args, repeat=1)``\n==============================================================================\n\nReference: `more_itertools.random_product \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_product\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').random_product(range(4), 'XYZ').collect()                  \n    ['c', 3, 'X']\n    \u003e\u003e\u003e Iter.random_product('abc', range(4), 'XYZ').collect()                  \n    ['c', 0, 'Z']\n    \u003e\u003e\u003e Iter('abc').random_product(range(0)).collect()\n    Traceback (most recent call last):\n        ...\n    IndexError: Cannot choose from an empty sequence\n    \u003e\u003e\u003e Iter.random_product(range(0)).collect()\n    Traceback (most recent call last):\n        ...\n    IndexError: Cannot choose from an empty sequence\n\n\n\n.. _Iter.random_permutation:\n\n\n``Iter.random_permutation(self, r=None)``\n=========================================\n\nReference: `more_itertools.random_permutation \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_permutation\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(5)).random_permutation().collect()                  \n    [2, 0, 4, 3, 1]\n    \u003e\u003e\u003e Iter(range(0)).random_permutation().collect()\n    []\n\n\n\n.. _Iter.random_combination:\n\n\n``Iter.random_combination(self, r)``\n====================================\n\nReference: `more_itertools.random_combination \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(5)).random_combination(3).collect()                  \n    [0, 1, 4]\n    \u003e\u003e\u003e Iter(range(5)).random_combination(0).collect()\n    []\n\n\n\n.. _Iter.random_combination_with_replacement:\n\n\n``Iter.random_combination_with_replacement(self, r)``\n=====================================================\n\nReference: `more_itertools.random_combination_with_replacement \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination_with_replacement\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(3)).random_combination_with_replacement(5).collect()                  \n    [0, 0, 1, 2, 2]\n    \u003e\u003e\u003e Iter(range(3)).random_combination_with_replacement(0).collect()\n    []\n\n\n\n.. _Iter.nth_combination:\n\n\n``Iter.nth_combination(self, r, index)``\n========================================\n\nReference: `more_itertools.nth_combination \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_combination\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(9)).nth_combination(3, 1).collect()\n    [0, 1, 3]\n    \u003e\u003e\u003e Iter(range(9)).nth_combination(3, 2).collect()\n    [0, 1, 4]\n    \u003e\u003e\u003e Iter(range(9)).nth_combination(3, 3).collect()\n    [0, 1, 5]\n    \u003e\u003e\u003e Iter(range(9)).nth_combination(4, 3).collect()\n    [0, 1, 2, 6]\n    \u003e\u003e\u003e Iter(range(9)).nth_combination(3, 7).collect()\n    [0, 2, 3]\n\n\n\n.. _Iter.always_iterable:\n\n\n``@classmethod Iter.always_iterable(cls, obj, base_type=(str, bytes)) -\u003e \"Iter\"``\n=================================================================================\n\nReference: `more_itertools.always_iterable \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable\u003e`_\n\n.. code-block: python\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.always_iterable([1, 2, 3]).collect()\n    [1, 2, 3]\n    \u003e\u003e\u003e Iter.always_iterable(1).collect()\n    [1]\n    \u003e\u003e\u003e Iter.always_iterable(None).collect()\n    []\n    \u003e\u003e\u003e Iter.always_iterable('foo').collect()\n    ['foo']\n    \u003e\u003e\u003e Iter.always_iterable(dict(a=1), base_type=dict).collect()\n    [{'a': 1}]\n\n\n\n.. _Iter.always_reversible:\n\n\n``Iter.always_reversible(self)``\n================================\n\nReference: `more_itertools.always_reversible \u003chttps://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_reversible\u003e`_\n\nThis is like ``reversed()`` but it also operates on things that\nwouldn't normally be reversible, like generators. It does this with\ninternal caching, so be careful with memory use.\n\n.. code-block: python\n\n    \u003e\u003e\u003e Iter('abc').always_reversible().collect()\n    ['c', 'b', 'a']\n    \u003e\u003e\u003e Iter(x for x in 'abc').always_reversible().collect()\n    ['c', 'b', 'a']\n\n\n\n.. _Iter.with_iter:\n\n\n``@classmethod Iter.with_iter(cls, context_manager)``\n=====================================================\n\nReference: `more_itertools.with_iter \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.with_iter\u003e`_\n\nNote: Any context manager which returns an iterable is a candidate for\nIter.with_iter_.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e import tempfile\n    \u003e\u003e\u003e with tempfile.TemporaryDirectory() as td:\n    ...     with open(td + 'text.txt', 'w') as f:\n    ...         f.writelines(['abc\\n', 'def\\n', 'ghi\\n'])\n    ...     Iter.with_iter(open(td + 'text.txt')).map(lambda x: x.upper()).collect()\n    ['ABC\\n', 'DEF\\n', 'GHI\\n']\n\nSee also: Iter.open_\n\n|flux| TODO: perhaps we should get rid of Iter.open_ and just use this?\n\n\n\n.. _Iter.iter_except:\n\n\n``@classmethod Iter.iter_except(cls, func, exception, first=None) -\u003e \"Iter\"``\n=============================================================================\n\nReference: `more_itertools.iter_except \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.iter_except\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e l = [0, 1, 2]\n    \u003e\u003e\u003e Iter.iter_except(l.pop, IndexError).collect()\n    [2, 1, 0]\n\n\n\n.. _Iter.locate:\n\n\n``Iter.locate(self, pred=bool, window_size=None) -\u003e \"Iter\"``\n============================================================\n\nReference: `more_itertools.locate \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.locate\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([0, 1, 1, 0, 1, 0, 0]).locate().collect()\n    [1, 2, 4]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(['a', 'b', 'c', 'b']).locate(lambda x: x == 'b').collect()\n    [1, 3]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]\n    \u003e\u003e\u003e pred = lambda *args: args == (1, 2, 3)\n    \u003e\u003e\u003e Iter(iterable).locate(pred=pred, window_size=3).collect()\n    [1, 5, 9]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from itertools import count\n    \u003e\u003e\u003e from more_itertools import seekable\n    \u003e\u003e\u003e source = (3 * n + 1 if (n % 2) else n // 2 for n in count())\n    \u003e\u003e\u003e it = Iter(source).seekable()\n    \u003e\u003e\u003e pred = lambda x: x \u003e 100\n    \u003e\u003e\u003e # TODO: can we avoid making two instances?\n    \u003e\u003e\u003e indexes = it.locate(pred=pred)\n    \u003e\u003e\u003e i = next(indexes)\n    \u003e\u003e\u003e it.seek(i).next()\n    106\n\n\n\n.. _Iter.rlocate:\n\n\n``Iter.rlocate(self, pred=bool, window_size=None) -\u003e \"Iter\"``\n=============================================================\n\nReference: `more_itertools.rlocate \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.rlocate\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter([0, 1, 1, 0, 1, 0, 0]).rlocate().collect()  # Truthy at 1, 2, and 4\n    [4, 2, 1]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e pred = lambda x: x == 'b'\n    \u003e\u003e\u003e Iter('abcb').rlocate(pred).collect()\n    [3, 1]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = [0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3]\n    \u003e\u003e\u003e pred = lambda *args: args == (1, 2, 3)\n    \u003e\u003e\u003e Iter(iterable).rlocate(pred=pred, window_size=3).collect()\n    [9, 5, 1]\n\n\n\n.. _Iter.replace:\n\n\n``Iter.replace(self, pred, substitutes, count=None, window_size=1) -\u003e \"Iter\"``\n==============================================================================\n\nReference: `more_itertools.replace \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.replace\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = [1, 1, 0, 1, 1, 0, 1, 1]\n    \u003e\u003e\u003e pred = lambda x: x == 0\n    \u003e\u003e\u003e substitutes = (2, 3)\n    \u003e\u003e\u003e Iter(iterable).replace(pred, substitutes).collect()\n    [1, 1, 2, 3, 1, 1, 2, 3, 1, 1]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = [1, 1, 0, 1, 1, 0, 1, 1, 0]\n    \u003e\u003e\u003e pred = lambda x: x == 0\n    \u003e\u003e\u003e substitutes = [None]\n    \u003e\u003e\u003e Iter(iterable).replace(pred, substitutes, count=2).collect()\n    [1, 1, None, 1, 1, None, 1, 1, 0]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = [0, 1, 2, 5, 0, 1, 2, 5]\n    \u003e\u003e\u003e window_size = 3\n    \u003e\u003e\u003e pred = lambda *args: args == (0, 1, 2)  # 3 items passed to pred\n    \u003e\u003e\u003e substitutes = [3, 4] # Splice in these items\n    \u003e\u003e\u003e Iter(iterable).replace(\n    ...     pred, substitutes, window_size=window_size\n    ... ).collect()\n    [3, 4, 5, 3, 4, 5]\n\n\n\n.. _Iter.numeric_range:\n\n\n``@classmethod Iter.numeric_range(cls, *args) -\u003e \"Iter\"``\n=========================================================\n\nReference: `more_itertools.numeric_range \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.numeric_range\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.numeric_range(3.5).collect()\n    [0.0, 1.0, 2.0, 3.0]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from decimal import Decimal\n    \u003e\u003e\u003e start = Decimal('2.1')\n    \u003e\u003e\u003e stop = Decimal('5.1')\n    \u003e\u003e\u003e Iter.numeric_range(start, stop).collect()\n    [Decimal('2.1'), Decimal('3.1'), Decimal('4.1')]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from fractions import Fraction\n    \u003e\u003e\u003e start = Fraction(1, 2)  # Start at 1/2\n    \u003e\u003e\u003e stop = Fraction(5, 2)  # End at 5/2\n    \u003e\u003e\u003e step = Fraction(1, 2)  # Count by 1/2\n    \u003e\u003e\u003e Iter.numeric_range(start, stop, step).collect()\n    [Fraction(1, 2), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1)]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.numeric_range(3, -1, -1.0).collect()\n    [3.0, 2.0, 1.0, 0.0]\n\n\n\n.. _Iter.side_effect:\n\n\n``Iter.side_effect(self, func, *args, chunk_size=None, before=None, after=None)``\n=================================================================================\n\nReference: `more_itertools.side_effect \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.side_effect\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e def f(item):\n    ...     if item == 3:\n    ...         raise Exception('got 3')\n    \u003e\u003e\u003e Iter.range(5).side_effect(f).consume()\n    Traceback (most recent call last):\n        ...\n    Exception: got 3\n\n.. code-block:: python\n\n    \u003e\u003e\u003e func = lambda item: print('Received {}'.format(item))\n    \u003e\u003e\u003e Iter.range(2).side_effect(func).consume()\n    Received 0\n    Received 1\n\nThis version of ``side_effect`` also allows extra args:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e func = lambda item, format_str='Received {}': print(format_str.format(item))\n    \u003e\u003e\u003e Iter.range(2).side_effect(func).consume()\n    Received 0\n    Received 1\n    \u003e\u003e\u003e func = lambda item, format_str='Received {}': print(format_str.format(item))\n    \u003e\u003e\u003e Iter.range(2).side_effect(func, 'Got {}').consume()\n    Got 0\n    Got 1\n\n\n\n\n.. _Iter.iterate:\n\n\n|source| ``@classmethod Iter.iterate(cls, func, start) -\u003e \"Iter\"``\n==================================================================\n\n\n\nReference: `more_itertools.iterate \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.iterate\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e def f(x):\n    ...     return x * 2\n    \u003e\u003e\u003e Iter.iterate(f, 1).take(4).collect()\n    [1, 2, 4, 8]\n\n\n.. _Iter.difference:\n\n\n``Iter.difference(self, func=operator.sub, *, initial=None) -\u003e \"Iter\"``\n=======================================================================\n\nReference: `more_itertools.difference \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=difference#more_itertools.difference\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = [0, 1, 3, 6, 10]\n    \u003e\u003e\u003e Iter(iterable).difference().collect()\n    [0, 1, 2, 3, 4]\n\n.. code-block:: python\n\n    \u003e\u003e\u003e iterable = [1, 2, 6, 24, 120]  # Factorial sequence\n    \u003e\u003e\u003e func = lambda x, y: x // y\n    \u003e\u003e\u003e Iter(iterable).difference(func).collect()\n    [1, 2, 3, 4, 5]\n\n\n\n.. _Iter.make_decorator:\n\n\n|flux| ``@classmethod Iter.make_decorator(cls, wrapping_func, result_index=0)``\n===============================================================================\n\n\n\nReference: `more_itertools.make_decorator \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=difference#more_itertools.make_decorator\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e chunker = Iter.make_decorator(Iter.chunked, result_index=0)\n    \u003e\u003e\u003e @chunker(3)\n    ... def iter_range(n):\n    ...     return range(n)\n    \u003e\u003e\u003e list(iter_range(9))\n    [[0, 1, 2], [3, 4, 5], [6, 7, 8]]\n    \u003e\u003e\u003e peekable_function = Iter.make_decorator(Iter.peekable)\n    \u003e\u003e\u003e @peekable_function()\n    ... def str_range(*args):\n    ...     return Iter(str(x) for x in range(*args))\n    \u003e\u003e\u003e it = str_range(1, 20, 2)\n    \u003e\u003e\u003e it.peek()\n    '1'\n    \u003e\u003e\u003e next(it)\n    '1'\n    \u003e\u003e\u003e it.peek()\n    '3'\n\n\n\n.. _Iter.time_limited:\n\n\n``Iter.time_limited(self, limit_seconds) -\u003e \"Iter\"``\n====================================================\n\nReference: `more_itertools.time_limited \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=time_limited#more_itertools.time_limited\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e from time import sleep\n    \u003e\u003e\u003e def generator():\n    ...     yield 1\n    ...     yield 2\n    ...     sleep(0.2)\n    ...     yield 3\n    \u003e\u003e\u003e Iter(generator()).time_limited(0.1).collect()\n    [1, 2]\n\n\n\n.. _Iter.consume:\n\n\n|sink| ``Iter.consume(self, n: Optional[int] = None) -\u003e \"Optional[Iter[T]]\"``\n=============================================================================\n\n\nIf n is not provided, the entire iterator is consumed and\n``None`` is returned. Otherwise, an iterator will *always* be\nreturned, even if n is greater than the number of items left in\nthe iterator.\n\nIn this example, the source has more elements than what we consume,\nso there will still be data available on the chain:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e range(10).consume(5).collect()\n    [5, 6, 7, 8, 9]\n\nWe can bump up the count of how many items can be consumed. Note that\neven though ``n`` is greater than the number of items in the source,\nit is still required to call Iter.collect_ to consume the remaining\nitems.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e range(10).consume(50).collect()\n    []\n\nFinally, if ``n`` is not provided, the entire stream is consumed.\nIn this scenario, Iter.collect_ would fail since nothing is being\nreturned from the consume call.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e assert range(10).consume() is None\n\n\n\n.. _Iter.tabulate:\n\n\n|source| ``@classmethod Iter.tabulate(cls, function, start=0) -\u003e \"Iter\"``\n=========================================================================\n\n\n\nReference: `more_itertools.tabulate \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.tabulate\u003e`_\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.tabulate(lambda x: x ** 2, start=-3).take(4).collect()\n    [9, 4, 1, 0]\n\n\n.. _Iter.repeatfunc:\n\n\n|source| ``@classmethod Iter.repeatfunc(cls, func, *args, times=None)``\n=======================================================================\n\n\nDocstring TODO\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter.repeatfunc(operator.add, 3, 5, times=4).collect()\n    [8, 8, 8, 8]\n\n\n\n.. _Iter.wrap:\n\n\n``Iter.wrap(self, ends: \"Sequence[T, T]\" = \"()\")``\n==================================================\n\nWrap the iterator with a start and end value. This is useful for\nadding brackets, parens, or other delimiters around the data.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').wrap('()').concat()\n    '(abc)'\n    \u003e\u003e\u003e Iter('abc').wrap('[]').concat()\n    '[abc]'\n    \u003e\u003e\u003e Iter('abc').wrap(['(((', ')))']).concat()\n    '(((abc)))'\n    \u003e\u003e\u003e Iter(range(5)).wrap([123, 123]).collect()\n    [123, 0, 1, 2, 3, 4, 123]\n    \u003e\u003e\u003e Iter(range(20, 25)).wrap([range(3), range(3)]).collect()\n    [0, 1, 2, 20, 21, 22, 23, 24, 0, 1, 2]\n\n\n\n.. _Iter.print:\n\n\n``Iter.print(self, template=\"{i}: {v}\") -\u003e \"Iter[T]\"``\n======================================================\n\nPrinting during the execution of an iterator. Mostly useful\nfor debugging. Returns another iterator instance through which\nthe original data is passed unchanged. This means you can include\na `print()` step as necessary to observe data during iteration.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('abc').print().collect()\n    0: a\n    1: b\n    2: c\n    ['a', 'b', 'c']\n\n    \u003e\u003e\u003e (\n    ...    Iter(range(5))\n    ...        .print('before filter {i}: {v}')\n    ...        .filter(lambda x: x \u003e 2)\n    ...        .print('after filter {i}: {v}')\n    ...        .collect()\n    ... )\n    before filter 0: 0\n    before filter 1: 1\n    before filter 2: 2\n    before filter 3: 3\n    after filter 0: 3\n    before filter 4: 4\n    after filter 1: 4\n    [3, 4]\n\n\n\n.. _Iter.from_queue:\n\n\n|source| ``@classmethod Iter.from_queue(cls, q: queue.Queue, timeout=None, sentinel=None)``\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 # This is where we consume data from the queue:\n    \u003e\u003e\u003e Iter.from_queue(q).filter(lambda x: 2 \u003c x \u003c 9).collect()\n    [3, 4, 5, 6, 7, 8]\n\nIf ``None`` had not been chained onto the data, the iterator would\nhave waited in Iter.collect_ forever.\n\n\n\n.. _Iter.into_queue:\n\n\n``Iter.into_queue(self, q: queue.Queue) -\u003e \"Iter[T]\"``\n======================================================\n\nThis is a sink, like Iter.collect_, that consumes data from\nan iterator chain and puts the data into the given queue.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e q = queue.Queue()\n    \u003e\u003e\u003e # This demonstrates the queue sink\n    \u003e\u003e\u003e range(5).into_queue(q).consume()\n    \u003e\u003e\u003e # Code below is only for verification\n    \u003e\u003e\u003e out = []\n    \u003e\u003e\u003e finished = False\n    \u003e\u003e\u003e while not finished:\n    ...     try:\n    ...         out.append(q.get_nowait())\n    ...     except queue.Empty:\n    ...         finished = True\n    \u003e\u003e\u003e out\n    [0, 1, 2, 3, 4]\n\n\n\n.. _Iter.send:\n\n\n|sink| ``Iter.send(self, collector: Generator, close_collector_when_done=False) -\u003e \"None\"``\n===========================================================================================\n\n\nSee also: `more_itertools.consumer \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.consumer\u003e`_\n\nSend data into a generator. You do not have to first call ``next()``\non the generator. Iter.send_ will do this for you.\n\n|warning| Look carefully at the examples below; you'll see that the\n``yield`` keyword is wrapped in a second set of parens, e.g.\n``output.append((yield))``. This is required!\n\nSimple case:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e output = []\n    \u003e\u003e\u003e def collector():\n    ...     while True:\n    ...         output.append((yield))\n    \u003e\u003e\u003e Iter.range(3).send(collector())\n    \u003e\u003e\u003e output\n    [0, 1, 2]\n\nNote that the generator is **not** closed by default after the iterable is\nexhausted. But this can be changed. If you choose to close the\ngenerator, use the parameter:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e output = []\n    \u003e\u003e\u003e def collector():\n    ...     while True:\n    ...         output.append((yield))\n    \u003e\u003e\u003e g = collector()\n    \u003e\u003e\u003e Iter.range(3).send(g, close_collector_when_done=True)\n    \u003e\u003e\u003e Iter.range(3).send(g)\n    Traceback (most recent call last):\n        ...\n    StopIteration\n\nThe default behaviour is that the generator is left open which means you\ncan keep using it for other iterators:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e output = []\n    \u003e\u003e\u003e def collector():\n    ...     while True:\n    ...         output.append((yield))\n    \u003e\u003e\u003e g = collector()\n    \u003e\u003e\u003e Iter.range(3).send(g)\n    \u003e\u003e\u003e Iter.range(10, 13).send(g)\n    \u003e\u003e\u003e Iter.range(100, 103).send(g)\n    \u003e\u003e\u003e output\n    [0, 1, 2, 10, 11, 12, 100, 101, 102]\n\n\nIf the generator is closed before the iteration is complete,\nyou'll get a ``StopIteration`` exception:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e output = []\n    \u003e\u003e\u003e def collector():\n    ...   for i in range(3):\n    ...       output.append((yield))\n    \u003e\u003e\u003e Iter.range(5).send(collector())\n    Traceback (most recent call last):\n        ...\n    StopIteration\n\nNote that Iter.send_ is a sink, so no further chaining is allowed.\n\n\n\n.. _Iter.send_also:\n\n\n``Iter.send_also(self, collector: Generator) -\u003e \"Iter\"``\n========================================================\n\nReference: `more_itertools.consumer \u003chttps://more-itertools.readthedocs.io/en/stable/api.html?highlight=numeric_range#more_itertools.consumer\u003e`_\n\nSome ideas around a reverse iterator as a sink. Usually you have\nfirst to \"send\" a ``None`` into a generator if you want to send\nmore values into it (or call ``next()`` on it), but we handle\nthat automatically.\n\nSimple case:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e output = []\n    \u003e\u003e\u003e def collector():\n    ...     while True:\n    ...         output.append((yield))\n    \u003e\u003e\u003e Iter.range(3).send_also(collector()).collect()\n    [0, 1, 2]\n    \u003e\u003e\u003e output\n    [0, 1, 2]\n\nHowever, if the caller already started the generator, that\nworks too:\n\n.. code-block:: python\n\n    \u003e\u003e\u003e output = []\n    \u003e\u003e\u003e def collector():\n    ...     while True:\n    ...         output.append((yield))\n    \u003e\u003e\u003e g = collector()\n    \u003e\u003e\u003e next(g)  # This \"starts\" the generator\n    \u003e\u003e\u003e Iter.range(3).send_also(g).collect()\n    [0, 1, 2]\n    \u003e\u003e\u003e output\n    [0, 1, 2]\n\nIf the generator is closed before the iteration is complete,\nyou'll get an exception (Python 3.7+):\n\n.. code-block:: python\n\n    \u003e\u003e\u003e output = []\n    \u003e\u003e\u003e def collector():\n    ...   for i in builtins.range(3):\n    ...       output.append((yield))\n    \u003e\u003e\u003e Iter.range(50).send_also(collector()).collect()                  \n    Traceback (most recent call last):\n        ...\n    RuntimeError\n\nNote that the above doesn't happen in Python \u003c 3.7 (which includes\npypy 7.3.1 that matches Python 3.6.9 compatibility). Instead, you\ncollect out the items up to until the point that the collector\nreturns; in this case, you'd get [0, 1, 2]. This change was made\nas part of `PEP 479 \u003chttps://www.python.org/dev/peps/pep-0479/\u003e`_.\n\nRegardless, for any Python it's recommended that your generator\nlive at least as long as the iterator feeding it.\n\n\n\n.. _Iter.sorted:\n\n\n|sink| |warning| ``Iter.sorted(self, key=None, reverse=False) -\u003e \"Iter[T]\"``\n============================================================================\n\n\n\nSimple wrapper for the ``sorted`` builtin.\n\n\nCalling this will read the entire stream before producing\nresults.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter('bac').sorted().collect()\n    ['a', 'b', 'c']\n    \u003e\u003e\u003e Iter('bac').sorted(reverse=True).collect()\n    ['c', 'b', 'a']\n    \u003e\u003e\u003e Iter('bac').zip([2, 1, 0]).sorted(key=lambda tup: tup[1]).collect()\n    [('c', 0), ('a', 1), ('b', 2)]\n\n\n\n.. _Iter.reversed:\n\n\n|sink| |warning| ``Iter.reversed(self) -\u003e \"Iter[T]\"``\n=====================================================\n\n\n\nSimple wrapper for the ``reversed`` builtin.\n\n\nCalling this will read the entire stream before producing\nresults.\n\n.. code-block:: python\n\n    \u003e\u003e\u003e Iter(range(4)).reversed().collect()\n    [3, 2, 1, 0]\n\n\n\n\nExperiments and Provisional Ideas\n#################################\n\n\n\n.. _IterDict:\n\n\n|flux| ``class IterDict(UserDict)``\n***********************************\n\n\n\nThe idea here was to make a custom dict where several of\nthe standard dict methods return ``Iter`` instances, which can then\nbe chained. I'm not sure if this will be kept yet.\n\n\n.. _IterDict.keys:\n\n\n``IterDict.keys(self) -\u003e \"Iter\"``\n=================================\n\n.. _IterDict.values:\n\n\n``IterDict.values(self) -\u003e \"Iter\"``\n===================================\n\n.. _IterDict.items:\n\n\n``IterDict.items(self) -\u003e \"Iter\"``\n==================================\n\n.. _IterDict.update:\n\n\n``IterDict.update(self, *args, **kwargs) -\u003e \"IterDict\"``\n========================================================\n\n.. _insert_separator:\n\n\n``insert_separator(iterable: Iterable[Any], glue: Any) -\u003e \"Iterable[Any]\"``\n***************************************************************************\nSimilar functionality can be obtained with, e.g.,\ninterleave, as in\n\n.. code-block:: python\n\n    \u003e\u003e\u003e result = Iter('caleb').interleave(Iter.repeat('x')).collect()\n    \u003e\u003e\u003e result == list('cxaxlxexbx')\n    True\n\nBut you'll see a trailing \"x\" there, which join avoids. join\nmakes sure to only add the glue separator if another element\nhas arrived.\n\nIt can handle strings without any special considerations, but it doesn't\ndo any special handling for bytes and bytearrays. For that, rather\nlook at `concat()`.\n\n\n\nRelated projects\n################\n\nIt turns out the idea of chaining iterators is not new. There are many\nlibraries that offer similar features:\n\n* My fork of a now-missing library: `chained-iterable \u003chttps://github.com/cjrh/chained-iterable\u003e`_.\n\n* `https://github.com/olirice/flupy \u003chttps://github.com/olirice/flupy\u003e`_\n\n* `https://github.com/ddstte/chiter \u003chttps://github.com/ddstte/chiter\u003e`_\n\n* `https://github.com/neverendingqs/pyiterable \u003chttps://github.com/neverendingqs/pyiterable\u003e`_\n\n* `https://github.com/alliefitter/iterable_collections \u003chttps://github.com/alliefitter/iterable_collections\u003e`_\n\n* `https://github.com/halprin/iterator-chain \u003chttps://github.com/halprin/iterator-chain\u003e`_\n\n* `https://github.com/jagill/python-chainz \u003chttps://github.com/jagill/python-chainz\u003e`_\n\n* `https://github.com/ZianVW/IterPipe \u003chttps://github.com/ZianVW/IterPipe\u003e`_\n\n* `https://github.com/Evelyn-H/iterchain \u003chttps://github.com/Evelyn-H/iterchain\u003e`_\n\n* `https://github.com/EntilZha/PyFunctional \u003chttps://github.com/EntilZha/PyFunctional\u003e`_\n\n* `https://github.com/dwt/fluent \u003chttps://github.com/dwt/fluent\u003e`_\n\nSomewhat related:\n\n* `https://github.com/jreese/aioitertools \u003chttps://github.com/jreese/aioitertools\u003e`_\n\n\nDev Instructions\n################\n\nSetup\n*****\n\n.. code-block:: shell\n\n    $ python -m venv venv\n    $ source venv/bin/activate\n    (venv) $ pip install -e .[dev,test]\n\nTesting\n*******\n\n.. code-block:: shell\n\n    (venv) $ pytest --cov\n\nDocumentation\n*************\n\nTo regenerate the documentation, file ``README.rst``:\n\n.. code-block:: shell\n\n    (venv) $ python regenerate_readme.py -m excitertools.py \u003e README.rst\n\nReleasing\n*********\n\nTo do a release, we're using `bumpymcbumpface \u003chttps://pypi.org/project/bumpymcbumpface/\u003e`_.\nMake sure that is set up correctly according to its own documentation. I \nlike to use `pipx \u003chttps://github.com/pipxproject/pipx\u003e`_ to install and \nmanage these kinds of tools.\n\n.. code-block:: shell\n\n    $ bumpymcbumpface --push-git --push-pypi\n\n|\n|\n\n-----\n\n|\n|\n\n    Work is a necessary evil to be avoided. \n    *Mark Twain*\n\n\n\n","funding_links":["https://github.com/sponsors/cjrh"],"categories":[],"sub_categories":[],"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"}