{"id":21874542,"url":"https://github.com/smoren/itertools-ts","last_synced_at":"2025-04-05T20:01:52.057Z","repository":{"id":97226881,"uuid":"608571209","full_name":"Smoren/itertools-ts","owner":"Smoren","description":"TypeScript Iteration Tools Library","archived":false,"fork":false,"pushed_at":"2025-03-12T14:15:48.000Z","size":1400,"stargazers_count":58,"open_issues_count":27,"forks_count":10,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-29T04:34:15.840Z","etag":null,"topics":["async-iterable","async-iterables","async-iteration","async-iterator","async-iterators","fluentinterface","generator","infinite-iteration","iterable","iterator","itertools","itertools-library","looping","loops","pipe","pipes","random-iteration","stream","streams","zip"],"latest_commit_sha":null,"homepage":"https://npmjs.com/package/itertools-ts","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Smoren.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"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}},"created_at":"2023-03-02T09:45:35.000Z","updated_at":"2025-03-12T14:15:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"1e6ea9ea-b65c-4219-af56-99f494bc292b","html_url":"https://github.com/Smoren/itertools-ts","commit_stats":{"total_commits":286,"total_committers":4,"mean_commits":71.5,"dds":0.07342657342657344,"last_synced_commit":"698e4c0217f70a51e9e2349a94f21b5685a0a5b8"},"previous_names":[],"tags_count":52,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smoren%2Fitertools-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smoren%2Fitertools-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smoren%2Fitertools-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smoren%2Fitertools-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Smoren","download_url":"https://codeload.github.com/Smoren/itertools-ts/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247393547,"owners_count":20931811,"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":["async-iterable","async-iterables","async-iteration","async-iterator","async-iterators","fluentinterface","generator","infinite-iteration","iterable","iterator","itertools","itertools-library","looping","loops","pipe","pipes","random-iteration","stream","streams","zip"],"created_at":"2024-11-28T07:12:40.546Z","updated_at":"2025-04-05T20:01:52.034Z","avatar_url":"https://github.com/Smoren.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# IterTools for TypeScript and JavaScript\n\n[![npm](https://img.shields.io/npm/v/itertools-ts.svg)](https://www.npmjs.com/package/itertools-ts)\n[![npm](https://img.shields.io/npm/dm/itertools-ts.svg?style=flat)](https://www.npmjs.com/package/itertools-ts)\n[![Coverage Status](https://coveralls.io/repos/github/Smoren/itertools-ts/badge.svg?branch=master\u0026rand=222)](https://coveralls.io/github/Smoren/itertools-ts?branch=master)\n![Build and test](https://github.com/Smoren/itertools-ts/actions/workflows/test_master.yml/badge.svg)\n[![Minified Size](https://badgen.net/bundlephobia/minzip/itertools-ts)](https://bundlephobia.com/result?p=itertools-ts)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n![IterTools Logo](docs/images/itertools-logo.png)\n\nInspired by Python — designed for TypeScript.\n\nFeatures\n--------\n\nIterTools makes you an iteration superstar by providing 3 types of tools:\n\n* Loop iteration tools\n* Stream iteration tools\n* Pipe iteration tools\n\n**Loop Iteration Tools Example**\n\n```typescript\nimport { multi } from 'itertools-ts';\n\nfor (const [letter, number] of multi.zip(['a', 'b'], [1, 2])) {\n  console.log(`${letter}${number}`);  // a1, b2\n}\n\n// Async example\nconst letters = ['a', 'b'].map((x) =\u003e Promise.resolve(x));\nconst numbers = [1, 2].map((x) =\u003e Promise.resolve(x));\n\nfor await (const [letter, number] of multi.zipAsync(letters, numbers)) {\n  console.log(`${letter}${number}`);  // a1, b2\n}\n```\n\n**Stream Iteration Tools Example**\n\n```typescript\nimport { Stream, AsyncStream } from 'itertools-ts';\n\nconst result1 = Stream.of([1, 1, 2, 2, 3, 4, 5])\n  .distinct()             // [1, 2, 3, 4, 5]\n  .map((x) =\u003e x**2)       // [1, 4, 9, 16, 25]\n  .filter((x) =\u003e x \u003c 10)  // [1, 4, 9]\n  .toSum();               // 14\n\n// Async example\nconst result2 = await AsyncStream.of([1, 1, 2, 2, 3, 4, 5].map((x) =\u003e Promise.resolve(x)))\n  .distinct()             // [1, 2, 3, 4, 5]\n  .map((x) =\u003e x**2)       // [1, 4, 9, 16, 25]\n  .filter((x) =\u003e x \u003c 10)  // [1, 4, 9]\n  .toSum();               // 14\n```\n\n[More about Streams](#Stream-and-Async-Stream)\n\n**Pipe Iteration Tools Example**\n\n```typescript\nimport { createPipe } from 'itertools-ts';\n\nconst pipe = createPipe(\n  set.distinct\u003cnumber\u003e,\n  (input) =\u003e single.map(input, (x) =\u003e x**2),\n  (input) =\u003e single.filter(input, (x) =\u003e x \u003c 10),\n  reduce.toSum,\n);\nconst result1 = pipe([1, 1, 2, 2, 3, 4, 5]); // 14\nconst result2 = pipe([1, 1, 1, 2, 2, 2]);    // 5\n\n// Async example\nconst asyncPipe = createPipe(\n  set.distinctAsync\u003cnumber\u003e,\n  (input) =\u003e single.mapAsync(input, (x) =\u003e x**2),\n  (input) =\u003e single.filterAsync(input, (x) =\u003e x \u003c 10),\n  reduce.toSumAsync,\n);\nconst result3 = await asyncPipe([1, 1, 2, 2, 3, 4, 5].map((x) =\u003e Promise.resolve(x))); // 14\nconst result4 = await asyncPipe([1, 1, 1, 2, 2, 2].map((x) =\u003e Promise.resolve(x)));    // 5\n\n// Another way to create pipes\nconst anotherPipe = createPipe()\n  .add(set.distinct\u003cnumber\u003e)\n  .add((input) =\u003e single.map(input, (x) =\u003e x**2))\n  .add((input) =\u003e single.filter(input, (x) =\u003e x \u003c 10))\n  .add(reduce.toSum);\n\nconst result5 = anotherPipe([1, 1, 2, 2, 3, 4, 5]); // 14\nconst result6 = anotherPipe([1, 1, 1, 2, 2, 2]);    // 5\n```\n\n[More about Pipes](#Pipes)\n\nAll functions work on iterable collections and iterators:\n* `Array`\n* `Set`\n* `Map`\n* `String`\n* `Generator`\n* `Iterable`\n* `Iterator`\n\nEvery function have an analog with \"Async\"-suffixed name for working with async iterable and iterators (e.g. `zip` and `zipAsync`):\n* `AsyncIterable`\n* `AsyncIterator`\n\nIf an asynchronous function takes other functions as input, they can also be asynchronous.\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst starWarsEpisodes = [1, 2, 3, 4, 5, 6, 7, 8, 9];\n\nfor await (const goodMovie of single.filterAsync(\n  starWarsEpisodes,\n  async (episode) =\u003e {\n    return Promise.resolve(episode \u003e 3 \u0026\u0026 episode \u003c 8);\n  }\n)) {\n  console.log(goodMovie);\n}\n// 4, 5, 6, 7\n```\n\nSetup\n-----\n\n```bash\nnpm i itertools-ts\n```\n\nQuick Reference\n---------------\n\n### Loop Iteration Tools\n\n#### Multi Iteration\n| Iterator                     | Description                                                                                                       | Sync Code Snippet                            | Async Code Snippet                                |\n|------------------------------|-------------------------------------------------------------------------------------------------------------------|----------------------------------------------|---------------------------------------------------|\n| [`chain`](#chain)            | Chain multiple iterables together                                                                                 | `multi.chain(list1, list2, ...)`             | `multi.chainAsync(list1, list2, ...)`             |\n| [`zip`](#zip)                | Iterate multiple collections simultaneously until the shortest iterator completes                                 | `multi.zip(list1, list2, ...)`               | `multi.zipAsync(list1, list2, ...)`               |\n| [`zipEqual`](#zip-equal)     | Iterate multiple collections of equal length simultaneously, error if lengths not equal                           | `multi.zipEqual(list1, list2, ...)`          | `multi.zipEqualAsync(list1, list2, ...)`          |\n| [`zipFilled`](#zip-filled)   | Iterate multiple collections simultaneously until the longest iterator completes (with filler for uneven lengths) | `multi.zipFilled(filler, list1, list2, ...)` | `multi.zipFilledAsync(filler, list1, list2, ...)` |\n| [`zipLongest`](#zip-longest) | Iterate multiple collections simultaneously until the longest iterator completes                                  | `multi.zipLongest(list1, list2, ...)`        | `multi.zipLongestAsync(list1, list2, ...)`        |\n\n#### Single Iteration\n| Iterator                                 | Description                                 | Sync Code Snippet                                       | Async Code Snippet                                           |\n|------------------------------------------|---------------------------------------------|---------------------------------------------------------|--------------------------------------------------------------|\n| [`chunkwise`](#chunkwise)                | Iterate by chunks                           | `single.chunkwise(data, chunkSize)`                     | `single.chunkwiseAsync(data, chunkSize)`                     |\n| [`chunkwiseOverlap`](#chunkwise-overlap) | Iterate by overlapped chunks                | `single.chunkwiseOverlap(data, chunkSize, overlapSize)` | `single.chunkwiseOverlapAsync(data, chunkSize, overlapSize)` |\n| [`compress`](#compress)                  | Filter out elements not selected            | `single.compress(data, selectors)`                      | `single.compressAsync(data, selectors)`                      |\n| [`dropWhile`](#drop-while)               | Drop elements while predicate is true       | `single.dropWhile(data, predicate)`                     | `single.dropWhileAsync(data, predicate)`                     |\n| [`enumerate`](#enumerate)                | Enumerates elements of collection           | `single.enumerate(data)`                                | `single.enumerateAsync(data)`                                |\n| [`filter`](#filter)                      | Filter for elements where predicate is true | `single.filter(data, predicate)`                        | `single.filterAsync(data, predicate)`                        |\n| [`flatMap`](#flat-map)                   | Map function onto items and flatten result  | `single.flatMap(data, mapper)`                          | `single.flatMapAsync(data, mapper)`                          |\n| [`flatten`](#flatten)                    | Flatten multidimensional iterable           | `single.flatten(data, [dimensions])`                    | `single.flattenAsync(data, [dimensions])`                    |\n| [`groupBy`](#group-by)                   | Group data by a common element              | `single.groupBy(data, groupKeyFunction, [itemKeyFunc])` | `single.groupByAsync(data, groupKeyFunction, [itemKeyFunc])` |\n| [`limit`](#limit)                        | Iterate up to a limit                       | `single.limit(data, limit)`                             | `single.limitAsync(data, limit)`                             |\n| [`keys`](#keys)                          | Iterate keys of key-value pairs             | `single.keys(data)`                                     | `single.keysAsync(data)`                                     |\n| [`map`](#map)                            | Map function onto each item                 | `single.map(data, mapper)`                              | `single.mapAsync(data, mapper)`                              |\n| [`pairwise`](#pairwise)                  | Iterate successive overlapping pairs        | `single.pairwise(data)`                                 | `single.pairwiseAsync(data)`                                 |\n| [`repeat`](#repeat)                      | Repeat an item a number of times            | `single.repeat(item, repetitions)`                      | `single.repeatAsync(item, repetitions)`                      |\n| [`skip`](#skip)                          | Iterate after skipping elements             | `single.skip(data, count, [offset])`                    | `single.skipAsync(data, count, [offset])`                    |\n| [`slice`](#slice)                        | Extract a slice of the iterable             | `single.slice(data, [start], [count], [step])`          | `single.sliceAsync(data, [start], [count], [step])`          |\n| [`sort`](#sort)                          | Iterate a sorted collection                 | `single.sort(data, [comparator])`                       | `single.sortAsync(data, [comparator])`                       |\n| [`takeWhile`](#take-while)               | Iterate elements while predicate is true    | `single.takeWhile(data, predicate)`                     | `single.takeWhileAsync(data, predicate)`                     |\n| [`values`](#values)                      | Iterate values of key-value pairs           | `single.values(data)`                                   | `single.valuesAsync(data)`                                   |\n\n#### Infinite Iteration\n| Iterator              | Description                | Code Snippet                      |\n|-----------------------|----------------------------|-----------------------------------|\n| [`count`](#Count)     | Count sequentially forever | `infinite.count([start], [step])` |\n| [`cycle`](#Cycle)     | Cycle through a collection | `infinite.cycle(iterable)`        |\n| [`repeat`](#Repeat-1) | Repeat an item forever     | `infinite.repeat(item)`           |\n\n#### Math Iteration\n| Iterator                                   | Description                     | Sync Code Snippet                                 | Async Code Snippet                                     |\n|--------------------------------------------|---------------------------------|---------------------------------------------------|--------------------------------------------------------|\n| [`runningAverage`](#Running-Average)       | Running average accumulation    | `math.runningAverage(numbers, [initialValue])`    | `math.runningAverageAsync(numbers, [initialValue])`    |\n| [`runningDifference`](#Running-Difference) | Running difference accumulation | `math.runningDifference(numbers, [initialValue])` | `math.runningDifferenceAsync(numbers, [initialValue])` |\n| [`runningMax`](#Running-Max)               | Running maximum accumulation    | `math.runningMax(numbers, [initialValue])`        | `math.runningMax(numbers, [initialValue])`             |\n| [`runningMin`](#Running-Min)               | Running minimum accumulation    | `math.runningMin(numbers, [initialValue])`        | `math.runningMinAsync(numbers, [initialValue])`        |\n| [`runningProduct`](#Running-Product)       | Running product accumulation    | `math.runningProduct(numbers, [initialValue])`    | `math.runningProductAsync(numbers, [initialValue])`    |\n| [`runningTotal`](#Running-Total)           | Running total accumulation      | `math.runningTotal(numbers, [initialValue])`      | `math.runningTotalAsync(numbers, [initialValue])`      |\n\n#### Reduce\n| Reducer                                | Description                                | Sync Code Snippet                             | Async Code Snippet                                 |\n|----------------------------------------|--------------------------------------------|-----------------------------------------------|----------------------------------------------------|\n| [`toAverage`](#To-Average)             | Mean average of elements                   | `reduce.toAverage(numbers)`                   | `reduce.toAverageAsync(numbers)`                   |\n| [`toCount`](#To-Count)                 | Reduce to length of iterable               | `reduce.toCount(data)`                        | `reduce.toCountAsync(data)`                        |\n| [`toFirst`](#To-First)                 | Reduce to its first value                  | `reduce.toFirst(data)`                        | `reduce.toFirstAsync(data)`                        |\n| [`toFirstAndLast`](#To-First-And-Last) | Reduce to its first and last values        | `reduce.toFirstAndLast(data)`                 | `reduce.toFirstAndLastAsync(data)`                 |\n| [`toLast`](#To-Last)                   | Reduce to its last value                   | `reduce.toLast(data)`                         | `reduce.toLastAsync(data)`                         |\n| [`toMax`](#To-Max)                     | Reduce to its greatest element             | `reduce.toMax(numbers, [compareBy])`          | `reduce.toMaxAsync(numbers, [compareBy])`          |\n| [`toMin`](#To-Min)                     | Reduce to its smallest element             | `reduce.toMin(numbers, [compareBy])`          | `reduce.toMinAsync(numbers, [compareBy])`          |\n| [`toMinMax`](#To-Min-Max)              | Reduce to its lower and upper bounds       | `reduce.toMinMax(numbers, [compareBy])`       | `reduce.toMinMaxAsync(numbers, [compareBy])`       |\n| [`toProduct`](#To-Product)             | Reduce to the product of its elements      | `reduce.toProduct(numbers)`                   | `reduce.toProductAsync(numbers)`                   |\n| [`toRange`](#To-Range)                 | Reduce to difference of max and min values | `reduce.toRange(numbers)`                     | `reduce.toRangeAsync(numbers)`                     |\n| [`toSum`](#To-Sum)                     | Reduce to the sum of its elements          | `reduce.toSum(numbers)`                       | `reduce.toSumAsync(numbers)`                       |\n| [`toValue`](#To-Value)                 | Reduce to value using callable reducer     | `reduce.toValue(data, reducer, initialValue)` | `reduce.toValueAsync(data, reducer, initialValue)` |\n\n#### Set and multiset Iteration\n| Iterator                                       | Description                            | Sync Code Snippet                                 | Async Code Snippet                                     |\n|------------------------------------------------|----------------------------------------|---------------------------------------------------|--------------------------------------------------------|\n| [`distinct`](#distinct)                        | Iterate only distinct items            | `set.distinct(data)`                              | `set.distinctAsync(data)`                              |\n| [`intersection`](#intersection)                | Intersection of iterables              | `set.intersection(...iterables)`                  | `set.intersectionAsync(...iterables)`                  |\n| [`partialIntersection`](#partial-intersection) | Partial intersection of iterables      | `set.partialIntersection(minCount, ...iterables)` | `set.partialIntersectionAsync(minCount, ...iterables)` |\n| [`symmetricDifference`](#symmetric-difference) | Symmetric difference of iterables      | `set.symmetricDifference(...iterables)`           | `set.symmetricDifferenceAsync(...iterables)`           |\n| [`union`](#union)                              | Union of iterables                     | `set.union(...iterables)`                         | `set.unionAsync(...iterables)`                         |\n\n#### Combinatorics\n| Iterator                                 | Description                            | Sync Code Snippet                             | Async Code Snippet                                 |\n|------------------------------------------|----------------------------------------|-----------------------------------------------|----------------------------------------------------|\n| [`cartesianProduct`](#cartesian-product) | Iterate cartesian product of iterables | `combinations.cartesianProduct(...iterables)` | `combinations.cartesianProductAsync(...iterables)` |\n| [`combinations`](#combinations)          | Combinations of iterables              | `combinations.combinations(data, length)`     | `combinations.combinationsAsync(data, length)`     |\n| [`permutations`](#permutations)          | Permutations of iterables              | `combinations.permutations(data, length)`     | `combinations.permutationsAsync(data, length)`     |\n\n#### Summary\n| Summary                                 | Description                                             | Sync Code Snippet                      | Async Code Snippet                          |\n|-----------------------------------------|---------------------------------------------------------|----------------------------------------|---------------------------------------------|\n| [`allMatch`](#all-match)                | True if all items are true according to predicate       | `summary.allMatch(data, predicate)`    | `summary.allMatchAsync(data, predicate)`    |\n| [`allUnique`](#all-unique)              | True if all elements in collection are unique           | `summary.allUnique(data)`              | `summary.allUniqueAsync(data)`              |\n| [`anyMatch`](#any-match)                | True if any item is true according to predicate         | `summary.anyMatch(data, predicate)`    | `summary.anyMatchAsync(data, predicate)`    |\n| [`exactlyN`](#exactly-n)                | True if exactly n items are true according to predicate | `summary.exactlyN(data, n, predicate)` | `summary.exactlyNAsync(data, n, predicate)` |\n| [`isAsyncIterable`](#is-async-iterable) | True if given data is async iterable                    | `summary.isAsyncIterable(data)`        | —                                           |\n| [`isIterable`](#is-iterable)            | True if given data is iterable                          | `summary.isIterable(data)`             | —                                           |\n| [`isIterator`](#is-iterator)            | True if given data is iterator                          | `summary.isIterator(data)`             | —                                           |\n| [`isReversed`](#is-reversed)            | True if iterable reverse sorted                         | `summary.isReversed(data)`             | `summary.isReversedAsync(data)`             |\n| [`isSorted`](#is-sorted)                | True if iterable sorted                                 | `summary.isSorted(data)`               | `summary.isSortedAsync(data)`               |\n| [`isString`](#is-string)                | True if given data is string                            | `summary.isString(data)`               | `summary.isStringAsync(data)`               |\n| [`noneMatch`](#none-match)              | True if none of items true according to predicate       | `summary.noneMatch(data, predicate)`   | `summary.noneMatchAsync(data, predicate)`   |\n| [`same`](#same)                         | True if collections are the same                        | `summary.same(...collections)`         | `summary.sameAsync(...collections)`         |\n| [`sameCount`](#same-count)              | True if collections have the same lengths               | `summary.sameCount(...collections)`    | `summary.sameCountAsync(...collections)`    |\n\n#### Transform\n| Iterator                                | Description                             | Sync Code Snippet                 | Async Code Snippet                |\n|-----------------------------------------|-----------------------------------------|-----------------------------------|-----------------------------------|\n| [`tee`](#tee)                           | Iterate duplicate iterables             | `transform.tee(data, count)`      | `transform.teeAsync(data, count)` |\n| [`toArray`](#to-array)                  | Transforms collection to array          | `transform.toArray(data)`         | `transform.toArrayAsync(data)`    |\n| [`toAsyncIterable`](#to-async-iterable) | Transforms collection to async iterable | `transform.toAsyncIterable(data)` | —                                 |\n| [`toAsyncIterator`](#to-async-iterator) | Transforms collection to async iterator | `transform.toAsyncIterator(data)` | —                                 |\n| [`toIterable`](#to-iterable)            | Transforms collection to iterable       | `transform.toIterable(data)`      | —                                 |\n| [`toIterator`](#to-iterator)            | Transforms collection to iterator       | `transform.toIterator(data)`      | —                                 |\n| [`toMap`](#to-map)                      | Transforms collection to map            | `transform.toMap(pairs)`          | `transform.toMapAsync(pairs)`     |\n| [`toSet`](#to-set)                      | Transforms collection to set            | `transform.toSet(data)`           | `transform.toSetAsync(data)`      |\n\n### Stream and AsyncStream Iteration Tools\n#### Stream Sources\n| Source                   | Description                         | Sync Code Snippet                 | Async Code Snippet                     |\n|--------------------------|-------------------------------------|-----------------------------------|----------------------------------------|\n| [`of`](#of)              | Create a stream from an iterable    | `Stream.of(iterable)`             | `AsyncStream.of(iterable)`             |\n| [`ofEmpty`](#of-empty)   | Create an empty stream              | `Stream.ofEmpty()`                | `AsyncStream.ofEmpty()`                |\n| [`ofCount`](#of-count)   | Create an infinite count stream     | `Stream.ofCount([start], [step])` | `AsyncStream.ofCount([start], [step])` |\n| [`ofCycle`](#of-cycle)   | Create an infinite cycle stream     | `Stream.ofCycle(iterable)`        | `AsyncStream.ofCycle(iterable)`        |\n| [`ofRepeat`](#of-repeat) | Create an infinite repeating stream | `Stream.ofRepeat(item)`           | `AsyncStream.ofRepeat(item)`           |\n\n#### Stream Operations\n| Operation                                               | Description                                                                               | Code Snippet                                                         |\n|---------------------------------------------------------|-------------------------------------------------------------------------------------------|----------------------------------------------------------------------|\n| [`cartesianProductWith`](#cartesian-product-with)       | Iterate cartesian product of iterable source with another iterable collections            | `stream.cartesianProductWith(...iterables)`                          |\n| [`chainWith`](#chain-with)                              | Chain iterable source withs given iterables together into a single iteration              | `stream.chainWith(...iterables)`                                     |\n| [`chunkwise`](#chunkwise-1)                             | Iterate by chunks                                                                         | `stream.chunkwise(chunkSize)`                                        |\n| [`chunkwiseOverlap`](#chunkwise-overlap-1)              | Iterate by overlapped chunks                                                              | `stream.chunkwiseOverlap(chunkSize, overlap)`                        |\n| [`combinations`](#combinations-1)                       | Combinations of the stream iterable                                                       | `stream.combinations(length)`                                        |\n| [`compress`](#compress-1)                               | Compress source by filtering out data not selected                                        | `stream.compress(selectors)`                                         |\n| [`distinct`](#distinct-1)                               | Filter out elements: iterate only unique items                                            | `stream.distinct()`                                                  |\n| [`dropWhile`](#drop-while-1)                            | Drop elements from the iterable source while the predicate function is true               | `stream.dropWhile(predicate)`                                        |\n| [`enumerate`](#enumerate-1)                             | Enumerates elements of stream                                                             | `stream.enumerate()`                                                 |\n| [`filter`](#filter-1)                                   | Filter for only elements where the predicate function is true                             | `stream.filter(predicate)`                                           |\n| [`flatMap`](#flat-map-1)                                | Map function onto elements and flatten result                                             | `stream.flatMap(mapper)`                                             |\n| [`flatten`](#flatten-1)                                 | Flatten multidimensional stream                                                           | `stream.flatten([dimensions])`                                       |\n| [`intersectionWith`](#intersection-with)                | Intersect stream and given iterables                                                      | `stream.intersectionWith(...iterables)`                              |\n| [`groupBy`](#group-by-1)                                | Group stram data by a common data element                                                 | `stream.groupBy(groupKeyFunction, [itemKeyFunc])`                    |\n| [`keys`](#keys-1)                                       | Iterate keys of key-value pairs from stream                                               | `stream.keys()`                                                      |\n| [`limit`](#limit-1)                                     | Limit the stream's iteration                                                              | `stream.limit(limit)`                                                |\n| [`map`](#map-1)                                         | Map function onto elements                                                                | `stream.map(mapper)`                                                 |\n| [`pairwise`](#pairwise-1)                               | Return pairs of elements from iterable source                                             | `stream.pairwise()`                                                  |\n| [`partialIntersectionWith`](#partial-intersection-with) | Partially intersect stream and given iterables                                            | `stream.partialIntersectionWith(minIntersectionCount, ...iterables)` |\n| [`permutations`](#permutations-1)                       | Permutations of the stream iterable                                                       | `stream.permutations(length)`                                        |\n| [`runningAverage`](#running-average-1)                  | Accumulate the running average (mean) over iterable source                                | `stream.runningAverage([initialValue])`                              |\n| [`runningDifference`](#running-difference-1)            | Accumulate the running difference over iterable source                                    | `stream.runningDifference([initialValue])`                           |\n| [`runningMax`](#running-max-1)                          | Accumulate the running max over iterable source                                           | `stream.runningMax([initialValue])`                                  |\n| [`runningMin`](#running-min-1)                          | Accumulate the running min over iterable source                                           | `stream.runningMin([initialValue])`                                  |\n| [`runningProduct`](#running-product-1)                  | Accumulate the running product over iterable source                                       | `stream.runningProduct([initialValue])`                              |\n| [`runningTotal`](#running-total-1)                      | Accumulate the running total over iterable source                                         | `stream.runningTotal([initialValue])`                                |\n| [`skip`](#skip-1)                                       | Skip some elements of the stream                                                          | `stream.skip(count, [offset])`                                       |\n| [`slice`](#slice-1)                                     | Extract a slice of the stream                                                             | `stream.slice([start], [count], [step])`                             |\n| [`sort`](#sort-1)                                       | Sorts the stream                                                                          | `stream.sort([comparator])`                                          |\n| [`symmetricDifferenceWith`](#symmetric-difference-with) | Symmetric difference of stream and given iterables                                        | `stream.symmetricDifferenceWith(...iterables)`                       |\n| [`takeWhile`](#take-while-1)                            | Return elements from the iterable source as long as the predicate is true                 | `stream.takeWhile(predicate)`                                        |\n| [`unionWith`](#union-with)                              | Union of stream and given iterables                                                       | `stream.union(...iterables)`                                         |\n| [`values`](#values-1)                                   | Iterate values of key-value pairs from stream                                             | `stream.values()`                                                    |\n| [`zipWith`](#zip-with)                                  | Iterate iterable source with another iterable collections simultaneously                  | `stream.zipWith(...iterables)`                                       |\n| [`zipEqualWith`](#zip-equal-with)                       | Iterate iterable source with another iterable collections of equal lengths simultaneously | `stream.zipEqualWith(...iterables)`                                  |\n| [`zipFilledWith`](#zip-filled-with)                     | Iterate iterable source with another iterable collections simultaneously (with filler)    | `stream.zipFilledWith(filler, ...iterables)`                         |\n| [`zipLongestWith`](#zip-longest-with)                   | Iterate iterable source with another iterable collections simultaneously                  | `stream.zipLongestWith(...iterables)`                                |\n\n#### Stream Terminal Operations\n##### Transformation Terminal Operations\n| Terminal Operation       | Description                                      | Code Snippet        |\n|--------------------------|--------------------------------------------------|---------------------|\n| [`tee`](#tee-1)          | Returns array of multiple identical Streams      | `stream.tee(count)` |\n| [`toArray`](#to-array-1) | Returns array of stream elements                 | `stream.toArray()`  |\n| [`toMap`](#to-map-1)     | Returns map of stream elements (key-value pairs) | `stream.toMap()`    |\n| [`toSet`](#to-set-1)     | Returns set of stream elements                   | `stream.toSet()`    |\n\n##### Reduction Terminal Operations\n| Terminal Operation                       | Description                                        | Code Snippet                            |\n|------------------------------------------|----------------------------------------------------|-----------------------------------------|\n| [`toAverage`](#to-average-1)             | Reduces stream to the mean average of its items    | `stream.toAverage()`                    |\n| [`toCount`](#to-count-1)                 | Reduces stream to its length                       | `stream.toCount()`                      |\n| [`toFirst`](#to-first-1)                 | Reduces stream to its first value                  | `stream.toFirst()`                      |\n| [`toFirstAndLast`](#to-first-and-last-1) | Reduces stream to its first and last values        | `stream.toFirstAndLast()`               |\n| [`toLast`](#to-last-1)                   | Reduces stream to its last value                   | `stream.toLast()`                       |\n| [`toMax`](#to-max-1)                     | Reduces stream to its max value                    | `stream.toMax([compareBy])`             |\n| [`toMin`](#to-min-1)                     | Reduces stream to its min value                    | `stream.toMin([compareBy])`             |\n| [`toMin`](#to-min-max-1)                 | Reduce stream to its lower and upper bounds        | `stream.toMinMax([compareBy])`          |\n| [`toProduct`](#to-product-1)             | Reduces stream to the product of its items         | `stream.toProduct()`                    |\n| [`toRange`](#to-range-1)                 | Reduces stream to difference of max and min values | `stream.toRange()`                      |\n| [`toSum`](#to-sum-1)                     | Reduces stream to the sum of its items             | `stream.toSum()`                        |\n| [`toValue`](#to-value-1)                 | Reduces stream like array.reduce() function        | `stream.toValue(reducer, initialValue)` |\n\n##### Summary Terminal Operations\n| Terminal Operation                  | Description                                                            | Code Snippet                           |\n|-------------------------------------|------------------------------------------------------------------------|----------------------------------------|\n| [`allMatch`](#all-match-1)          | Returns true if all items in stream match predicate                    | `stream.allMatch(predicate)`           |\n| [`allUnique`](#all-unique-1)        | Returns true if all elements of stream are unique                      | `stream.allUnique(predicate)`          |\n| [`anyMatch`](#any-match-1)          | Returns true if any item in stream matches predicate                   | `stream.anyMatch(predicate)`           |\n| [`exactlyN`](#exactly-n-1)          | Returns true if exactly n items are true according to predicate        | `stream.exactlyN(n, predicate)`        |\n| [`isReversed`](#is-reversed-1)      | Returns true if stream is sorted in reverse descending order           | `stream.isReversed()`                  |\n| [`isSorted`](#is-sorted-1)          | Returns true if stream is sorted in ascending order                    | `stream.isSorted()`                    |\n| [`noneMatch`](#none-match-1)        | Returns true if none of the items in stream match predicate            | `stream.noneMatch(predicate)`          |\n| [`sameWith`](#same-with)            | Returns true if stream and all given collections are the same          | `stream.sameWith(...collections)`      |\n| [`sameCountWith`](#same-count-with) | Returns true if stream and all given collections have the same lengths | `stream.sameCountWith(...collections)` |\n\n#### Stream Debug Operations\n| Debug Operation              | Description                                    | Code Snippet                  |\n|------------------------------|------------------------------------------------|-------------------------------|\n| [`peek`](#peek)              | Peek at each element between stream operations | `stream.peek(peekFunc)`       |\n| [`peekStream`](#peek-stream) | Peek at the entire stream between operations   | `stream.peekStream(peekFunc)` |\n\nUsage\n-----\n\n## Multi Iteration\n### Chain\nChain multiple iterables together into a single continuous sequence.\n\n```\nfunction* chain\u003cT\u003e(\n  ...iterables: Array\u003cIterable\u003cT\u003e | Iterator\u003cT\u003e\u003e\n): Iterable\u003cT\u003e\n```\n```typescript\nimport { multi } from 'itertools-ts';\n\nconst prequels = ['Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith'];\nconst originals = ['A New Hope', 'Empire Strikes Back', 'Return of the Jedi'];\n\nfor (const movie of multi.chain(prequels, originals)) {\n  console.log(movie);\n}\n// 'Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith', 'A New Hope', 'Empire Strikes Back', 'Return of the Jedi'\n```\n\n### Zip\nIterate multiple iterable collections simultaneously.\n\n```\nfunction* zip\u003cT extends Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e\u003e(\n  ...iterables: T\n): Iterable\u003cZipTuple\u003cT, never\u003e\u003e\n```\n\n```typescript\nimport { multi } from 'itertools-ts';\n\nconst languages = ['PHP', 'Python', 'Java', 'Go'];\nconst mascots = ['elephant', 'snake', 'bean', 'gopher'];\n\nfor (const [language, mascot] of multi.zip(languages, mascots)) {\n  console.log(`The ${language} language mascot is an ${mascot}.`);\n}\n// The PHP language mascot is an elephant.\n// ...\n```\n\nZip works with multiple iterable inputs - not limited to just two.\n```typescript\nimport { multi } from 'itertools-ts';\n\nconst names          = ['Ryu', 'Ken', 'Chun Li', 'Guile'];\nconst countries      = ['Japan', 'USA', 'China', 'USA'];\nconst signatureMoves = ['hadouken', 'shoryuken', 'spinning bird kick', 'sonic boom'];\n\nfor (const [name, country, signatureMove] of multi.zip(names, countries, signatureMoves)) {\n  const streetFighter = new StreetFighter(name, country, signatureMove);\n}\n```\nNote: For uneven lengths, iteration stops when the shortest iterable is exhausted.\n\n### Zip Filled\nIterate multiple iterable collections simultaneously.\n\n```\nfunction* zipFilled\u003cT extends Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e, F\u003e(\n  filler: F,\n  ...iterables: T\n): Iterable\u003cZipTuple\u003cT, F\u003e\u003e\n```\n\nFor uneven lengths, the exhausted iterables will produce `filler` value for the remaining iterations.\n\n```typescript\nimport { multi } from 'itertools-ts';\n\nconst letters = ['A', 'B', 'C'];\nconst numbers = [1, 2];\n\nfor (const [letter, number] of multi.zipFilled('filler', letters, numbers)) {\n  // ['A', 1], ['B', 2], ['C', 'filler']\n}\n```\n\n### Zip Longest\nIterate multiple iterable collections simultaneously.\n\n```\nfunction* zipLongest\u003cT extends Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e\u003e(\n  ...iterables: T\n): Iterable\u003cZipTuple\u003cT, undefined\u003e\u003e\n```\n\nFor uneven lengths, the exhausted iterables will produce `undefined` for the remaining iterations.\n\n```typescript\nimport { multi } from 'itertools-ts';\n\nconst letters = ['A', 'B', 'C'];\nconst numbers = [1, 2];\n\nfor (const [letter, number] of multi.zipLongest(letters, numbers)) {\n  // ['A', 1], ['B', 2], ['C', undefined]\n}\n```\n\n### Zip Equal\nIterate multiple iterable collections with equal lengths simultaneously.\n\nThrows `LengthException` if lengths are not equal, meaning that at least one iterator ends before the others.\n\n```\nfunction* zipEqual\u003cT extends Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e\u003e(\n  ...iterables: T\n): Iterable\u003cZipTuple\u003cT, never\u003e\u003e\n```\n\n```typescript\nimport { multi } from 'itertools-ts';\n\nconst letters = ['A', 'B', 'C'];\nconst numbers = [1, 2, 3];\n\nfor (const [letter, number] of multi.zipEqual(letters, numbers)) {\n    // ['A', 1], ['B', 2], ['C', 3]\n}\n```\n\n## Single Iteration\n### Chunkwise\nReturn elements in chunks of a certain size.\n\n```\nfunction* chunkwise\u003cT\u003e(\n  data: Iterable\u003cT\u003e|Iterator\u003cT\u003e,\n  chunkSize: number,\n): Iterable\u003cArray\u003cT\u003e\u003e\n```\n\nChunk size must be at least 1.\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst movies = [\n    'Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith',\n    'A New Hope', 'Empire Strikes Back', 'Return of the Jedi',\n    'The Force Awakens', 'The Last Jedi', 'The Rise of Skywalker',\n];\nconst trilogies = [];\n\nfor (const trilogy of single.chunkwise(movies, 3)) {\n    trilogies.push(trilogy);\n}\n// [\n//     ['Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith'],\n//     ['A New Hope', 'Empire Strikes Back', 'Return of the Jedi'],\n//     ['The Force Awakens', 'The Last Jedi', 'The Rise of Skywalker]',\n// ]\n```\n\n### Chunkwise Overlap\nReturn overlapped chunks of elements.\n\n```\nfunction* chunkwiseOverlap\u003cT\u003e(\n  data: Iterable\u003cT\u003e|Iterator\u003cT\u003e,\n  chunkSize: number,\n  overlapSize: number,\n  includeIncompleteTail: boolean = true,\n): Iterable\u003cArray\u003cT\u003e\u003e\n```\n\n* Chunk size must be at least 1.\n* Overlap size must be less than chunk size.\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n\nfor (const chunk of single.chunkwiseOverlap(numbers, 3, 1)) {\n  // [1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9], [9, 10]\n}\n```\n\n### Compress\nCompress an iterable by filtering out data that is not selected.\n\n```\nfunction* compress\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  selectors: Iterable\u003cnumber|boolean\u003e | Iterator\u003cnumber|boolean\u003e\n): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst movies = [\n  'Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith',\n  'A New Hope', 'Empire Strikes Back', 'Return of the Jedi',\n  'The Force Awakens', 'The Last Jedi', 'The Rise of Skywalker'\n];\nconst goodMovies = [0, 0, 0, 1, 1, 1, 1, 0, 0];\n\nfor (const goodMovie of single.compress(movies, goodMovies)) {\n  console.log(goodMovie);\n}\n// 'A New Hope', 'Empire Strikes Back', 'Return of the Jedi', 'The Force Awakens'\n```\n\n### Drop While\nDrop elements from the iterable while the predicate function is true.\n\nOnce the predicate function returns false once, all remaining elements are returned.\n\n```\nfunction* dropWhile\u003cT\u003e(\n  data: Iterable\u003cT\u003e|Iterator\u003cT\u003e,\n  predicate: (item: T) =\u003e boolean\n): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst scores    = [50, 60, 70, 85, 65, 90];\nconst predicate = (x) =\u003e x \u003c 70;\n\nfor (const score of single.dropWhile(scores, predicate)) {\n  console.log(score);\n}\n// 70, 85, 65, 90\n```\n\n### Enumerate\nEnumerates elements of given collection.\n\n```\nfunction* enumerate\u003cT\u003e(data: Iterable\u003cT\u003e|Iterator\u003cT\u003e): Iterable\u003c[number, T]\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst letters = ['a', 'b', 'c', 'd', 'e'];\n\nfor (const item of single.enumerate(letters)) {\n  // [[0, 'a'], [1, 'b'], [2, 'c'], [3, 'd'], [4, 'e']]\n}\n```\n\n### Filter\nFilter out elements from the iterable only returning elements where the predicate function is true.\n\n```\nfunction* filter\u003cT\u003e(\n  data: Iterable\u003cT\u003e|Iterator\u003cT\u003e,\n  predicate: (datum: T) =\u003e boolean,\n): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst starWarsEpisodes = [1, 2, 3, 4, 5, 6, 7, 8, 9];\nconst goodMoviePredicate = (episode) =\u003e episode \u003e 3 \u0026\u0026 episode \u003c 8;\n\nfor (const goodMovie of single.filter(starWarsEpisodes, goodMoviePredicate)) {\n  console.log(goodMovie);\n}\n// 4, 5, 6, 7\n```\n\n### Flat Map\nMap a function only the elements of the iterable and then flatten the results.\n\n```\nfunction* flatMap\u003cTInput, TOutput\u003e(\n  data: Iterable\u003cTInput\u003e|Iterator\u003cTInput\u003e,\n  mapper: FlatMapper\u003cTInput, TOutput\u003e,\n): Iterable\u003cTOutput\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst data = [1, 2, 3, 4, 5];\nconst mapper = (item) =\u003e [item, -item];\n\nfor (number of single.flatMap(data, mapper)) {\n  console.log(number);\n}\n// 1 -1 2 -2 3 -3 4 -4 5 -5\n```\n\n### Flatten\nFlatten a multidimensional iterable.\n\n```\nfunction* flatten(\n  data: Iterable\u003cunknown\u003e|Iterator\u003cunknown\u003e,\n  dimensions: number = Infinity,\n): Iterable\u003cunknown\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst multidimensional = [1, [2, 3], [4, 5]];\n\nconst flattened = [];\nfor (const number of single.flatten(multidimensional)) {\n    flattened.push(number);\n}\n// [1, 2, 3, 4, 5]\n```\n\n### Group By\nGroup data by a common data element.\n\nIterate pairs of group name and collection of grouped items.\n\n```\nexport function* groupBy\u003c\n  T,\n  TItemKeyFunction extends ((item: T) =\u003e string) | undefined,\n  TResultItem extends TItemKeyFunction extends undefined ? [string, Array\u003cT\u003e] : [string, Record\u003cstring, T\u003e]\n\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  groupKeyFunction: (item: T) =\u003e string,\n  itemKeyFunction?: TItemKeyFunction\n): Iterable\u003cTResultItem\u003e\n```\n\n* The `groupKeyFunction` determines the key to group elements by.\n* The optional `itemKeyFunction` allows custom indexes within each group member.\n* Collection of grouped items may be an array or an object (depends on presence of `itemKeyFunction` param).\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst cartoonCharacters = [\n    ['Garfield', 'cat'],\n    ['Tom', 'cat'],\n    ['Felix', 'cat'],\n    ['Heathcliff', 'cat'],\n    ['Snoopy', 'dog'],\n    ['Scooby-Doo', 'dog'],\n    ['Odie', 'dog'],\n    ['Donald', 'duck'],\n    ['Daffy', 'duck'],\n];\n\nconst charactersGroupedByAnimal = {};\nfor (const [animal, characters] of single.groupBy(cartoonCharacters, (x) =\u003e x[1])) {\n    charactersGroupedByAnimal[animal] = characters;\n}\n/*\n{\n  cat: [\n    ['Garfield', 'cat'],\n    ['Tom', 'cat'],\n    ['Felix', 'cat'],\n    ['Heathcliff', 'cat'],\n  ],\n  dog: [\n    ['Snoopy', 'dog'],\n    ['Scooby-Doo', 'dog'],\n    ['Odie', 'dog'],\n  ],\n  duck: [\n    ['Donald', 'duck'],\n    ['Daffy', 'duck'],\n  ],\n}\n*/\n```\n\n### Keys\nIterate keys of key-value pairs.\n\n```\nfunction* keys\u003cTKey, TValue\u003e(\n  collection: Iterable\u003c[TKey, TValue]\u003e|Iterator\u003c[TKey, TValue]\u003e,\n): Iterable\u003cTKey\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst dict = new Map([['a', 1], ['b', 2], ['c', 3]]);\n\nfor (const key of single.keys(dict)) {\n  console.log(key);\n}\n// 'a', 'b', 'c'\n```\n\n### Limit\nIterate up to a limit.\n\nStops even if more data available if limit reached.\n\n```\nfunction* limit\u003cT\u003e(data: Iterable\u003cT\u003e|Iterator\u003cT\u003e, count: number): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst matrixMovies = ['The Matrix', 'The Matrix Reloaded', 'The Matrix Revolutions', 'The Matrix Resurrections'];\nconst limit = 1;\n\nfor (const goodMovie of single.limit(matrixMovies, limit)) {\n    console.log(goodMovie);\n}\n// 'The Matrix' (and nothing else)\n```\n\n### Map\nMap a function onto each element.\n\n```\nfunction* map\u003cTInput, TOutput\u003e(\n  data: Iterable\u003cTInput\u003e|Iterator\u003cTInput\u003e,\n  mapper: (datum: TInput) =\u003e TOutput,\n): Iterable\u003cTOutput\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst grades = [100, 99, 95, 98, 100];\nconst strictParentsOpinion = (g) =\u003e (g === 100) ? 'A' : 'F';\n\nfor (const actualGrade of single.map(grades, strictParentsOpinion)) {\n  console.log(actualGrade);\n}\n// A, F, F, F, A\n```\n\n### Pairwise\nReturns successive overlapping pairs.\n\nReturns empty generator if given collection contains fewer than 2 elements.\n\n```\nfunction* pairwise\u003cT\u003e(data: Iterable\u003cT\u003e|Iterator\u003cT\u003e): Iterable\u003cPair\u003cT\u003e\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst friends = ['Ross', 'Rachel', 'Chandler', 'Monica', 'Joey', 'Phoebe'];\n\nfor (const [leftFriend, rightFriend] of single.pairwise(friends)) {\n  console.log(`${leftFriend} and ${rightFriend}`);\n}\n// Ross and Rachel, Rachel and Chandler, Chandler and Monica, ...\n```\n\n### Repeat\nRepeat an item.\n\n```\nfunction* repeat\u003cT\u003e(item: T, repetitions: number): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\ndata = 'Beetlejuice';\nrepetitions = 3;\n\nfor (const repeated of single.repeat(data, repetitions)) {\n  console.log(repeated);\n}\n// 'Beetlejuice', 'Beetlejuice', 'Beetlejuice'\n```\n\n### Skip\nSkip n elements in the iterable after optional offset offset.\n\n```\nfunction* skip\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  count: number,\n  offset: number = 0\n): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst movies = [\n    'The Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith',\n    'A New Hope', 'The Empire Strikes Back', 'Return of the Jedi',\n    'The Force Awakens', 'The Last Jedi', 'The Rise of Skywalker'\n];\n\nconst prequelsRemoved = [];\nfor (const nonPrequel of Single.skip(movies, 3)) {\n  prequelsRemoved.push(nonPrequel);\n} // Episodes IV - IX\n\nconst onlyTheBest = [];\nfor (const nonSequel of Single.skip(prequelsRemoved, 3, 3)) {\n  onlyTheBest.push(nonSequel);\n}\n// 'A New Hope', 'The Empire Strikes Back', 'Return of the Jedi'\n```\n\n### Slice\nExtract a slice of the iterable.\n\n```\nfunction* slice\u003cT\u003e(\n  data: Iterable\u003cT\u003e|Iterator\u003cT\u003e,\n  start: number = 0,\n  count?: number,\n  step: number = 1,\n): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst olympics = [1992, 1994, 1996, 1998, 2000, 2002, 2004, 2006, 2008, 2010, 2012, 2014, 2016, 2018, 2020, 2022];\nconst winterOlympics = [];\n\nfor (const winterYear of single.slice(olympics, 1, 8, 2)) {\n    winterOlympics.push(winterYear);\n}\n// [1994, 1998, 2002, 2006, 2010, 2014, 2018, 2022]\n```\n\n### Sort\nIterate the collection sorted.\n\n```\nfunction* sort\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  comparator?: Comparator\u003cT\u003e,\n): Iterable\u003cT\u003e\n```\n\nUses default sorting if optional comparator function not provided.\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst data = [3, 4, 5, 9, 8, 7, 1, 6, 2];\n\nfor (const datum of single.sort(data)) {\n  console.log(datum);\n}\n// 1, 2, 3, 4, 5, 6, 7, 8, 9\n```\n\n### Take While\nReturn elements from the iterable as long as the predicate is true.\n\nStops iteration as soon as the predicate returns false, even if other elements later on would eventually return true (different from filterTrue).\n\n```\nfunction* takeWhile\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  predicate: (item: T) =\u003e boolean\n): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst prices = [0, 0, 5, 10, 0, 0, 9];\nconst isFree = (price) =\u003e price == 0;\n\nfor (const freePrice of single.takeWhile(prices, isFree)) {\n  console.log(freePrice);\n}\n// 0, 0\n```\n\n### Values\nIterate values of key-value pairs.\n\n```\nfunction* values\u003cTKey, TValue\u003e(\n  collection: Iterable\u003c[TKey, TValue]\u003e|Iterator\u003c[TKey, TValue]\u003e,\n): Iterable\u003cTValue\u003e\n```\n\n```typescript\nimport { single } from 'itertools-ts';\n\nconst dict = new Map([['a', 1], ['b', 2], ['c', 3]]);\n\nfor (const value of single.keys(dict)) {\n  console.log(value);\n}\n// 1, 2, 3\n```\n\n## Infinite Iteration\n\n### Count\nCount sequentially forever.\n\n```\nfunction* count(start: number = 1, step: number = 1): Iterable\u003cnumber\u003e\n```\n\n```typescript\nimport { infinite } from 'itertools-ts';\n\nfor (const i of infinite.count()) {\n  console.log(i);\n}\n// 1, 2, 3, 4, 5, ...\n```\n\n### Cycle\nCycle through the elements of a collection sequentially forever.\n\n```\nfunction* cycle\u003cT\u003e(iterable: Iterable\u003cT\u003e | Iterator\u003cT\u003e): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { infinite } from 'itertools-ts';\n\nfor (const item of infinite.cycle(['rock', 'paper', 'scissors'])) {\n  console.log(item);\n}\n// 'rock', 'paper', 'scissors', 'rock', 'paper', 'scissors', 'rock', ...\n```\n\n### Repeat\nRepeat an item forever.\n\n```\nfunction* repeat\u003cT\u003e(item: T): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { infinite } from 'itertools-ts';\n\nfor (const item of infinite.repeat('bla')) {\n  console.log(item);\n}\n// bla, bla, bla, bla, bla, ...\n```\n\n## Math Iteration\n\n### Running Average\nAccumulate the running average over a list of numbers.\n\n```\nfunction* runningAverage(\n  numbers: Iterable\u003cNumeric\u003e | Iterator\u003cNumeric\u003e,\n  initialValue?: number\n): Iterable\u003cnumber\u003e\n```\n\n```typescript\nimport { math } from 'itertools-ts';\n\nconst grades = [100, 80, 80, 90, 85];\n\nfor (const runningAverage of math.runningAverage(grades)) {\n  console.log(runningAverage);\n}\n// 100, 90, 86.667, 87.5, 87\n```\n\n### Running Difference\nAccumulate the running difference over a list of numbers.\n\n```\nfunction* runningDifference(\n  numbers: Iterable\u003cNumeric\u003e | Iterator\u003cNumeric\u003e,\n  initialValue?: number\n): Iterable\u003cnumber\u003e\n```\n\n```typescript\nimport { math } from 'itertools-ts';\n\nconst credits = [1, 2, 3, 4, 5];\n\nfor (const runningDifference of math.runningDifference(credits)) {\n    console.log(runningDifference);\n}\n// -1, -3, -6, -10, -15\n```\n\nProvide an optional initial value to lead off the running difference.\n\n```typescript\nimport { math } from 'itertools-ts';\n\nconst dartsScores   = [50, 50, 25, 50];\nconst startingScore = 501;\n\nfor (const runningScore of math.runningDifference(dartsScores, startingScore)) {\n  console.log(runningScore);\n}\n// 501, 451, 401, 376, 326\n```\n\n### Running Max\nAccumulate the running maximum over a list of numbers.\n\n```\nfunction* runningMax(\n  numbers: Iterable\u003cNumeric\u003e | Iterator\u003cNumeric\u003e,\n  initialValue?: number\n): Iterable\u003cnumber\u003e\n```\n\n```typescript\nimport { math } from 'itertools-ts';\n\nconst numbers = [1, 2, 1, 3, 5];\n\nfor (const runningMax of math.runningMax(numbers)) {\n  console.log(runningMax);\n}\n// 1, 2, 2, 3, 5\n```\n\n### Running Min\nAccumulate the running minimum over a list of numbers.\n\n```\nfunction* runningMin(\n  numbers: Iterable\u003cNumeric\u003e | Iterator\u003cNumeric\u003e,\n  initialValue?: number\n): Iterable\u003cnumber\u003e\n```\n\n```typescript\nimport { math } from 'itertools-ts';\n\nconst numbers = [3, 4, 2, 5, 1];\n\nfor (const runningMin of math.runningMin(numbers)) {\n    console.log(runningMin);\n}\n// 3, 3, 2, 2, 1\n```\n\n### Running Product\nAccumulate the running product over a list of numbers.\n\n```\nfunction* runningProduct(\n  numbers: Iterable\u003cNumeric\u003e | Iterator\u003cNumeric\u003e,\n  initialValue?: number\n): Iterable\u003cnumber\u003e\n```\n\n```typescript\nimport { math } from 'itertools-ts';\n\nconst numbers = [1, 2, 3, 4, 5];\n\nfor (const runningProduct of math.runningProduct(numbers)) {\n  console.log(runningProduct);\n}\n// 1, 2, 6, 24, 120\n```\n\nProvide an optional initial value to lead off the running product.\n\n```typescript\nimport { math } from 'itertools-ts';\n\nconst numbers = [1, 2, 3, 4, 5];\nconst initialValue = 5;\n\nfor (const runningProduct of math.runningProduct(numbers, initialValue)) {\n  console.log(runningProduct);\n}\n// 5, 5, 10, 30, 120, 600\n```\n\n### Running Total\nAccumulate the running total over a list of numbers.\n\n```\nfunction* runningTotal(\n  numbers: Iterable\u003cNumeric\u003e | Iterator\u003cNumeric\u003e,\n  initialValue?: number\n): Iterable\u003cnumber\u003e\n```\n\n```typescript\nimport { math } from 'itertools-ts';\n\nconst prices = [1, 2, 3, 4, 5];\n\nfor (const runningTotal of math.runningTotal(prices)) {\n    console.log(runningTotal);\n}\n// 1, 3, 6, 10, 15\n```\n\nProvide an optional initial value to lead off the running total.\n```typescript\nimport { math } from 'itertools-ts';\n\nconst prices = [1, 2, 3, 4, 5];\nconst initialValue = 5;\n\nfor (const runningTotal of math.runningTotal(prices, initialValue)) {\n  console.log(runningTotal);\n}\n// 5, 6, 8, 11, 15, 20\n```\n\n## Reduce\n### To Average\nReduces to the mean average.\n\nReturns `undefined` if collection is empty.\n\n```\nfunction toAverage(\n  data: Iterable\u003cnumber\u003e | Iterator\u003cnumber\u003e,\n): number | undefined\n```\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst grades = [100, 90, 95, 85, 94];\n\nconst finalGrade = reduce.toAverage(numbers);\n// 92.8\n```\n\n### To Count\nReduces iterable to its length.\n\n```\nfunction toCount(data: Iterable\u003cunknown\u003e|Iterator\u003cunknown\u003e): number\n```\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst data = [1, 2, 3];\n\nconst length = reduce.toCount(data);\n// 3\n```\n\n### To First\nReduces iterable to its first element.\n\n```\nfunction toFirst\u003cT\u003e(data: Iterable\u003cT\u003e | Iterator\u003cT\u003e): T\n```\n\nThrows `LengthException` if collection is empty.\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst medals = ['gold', 'silver', 'bronze'];\n\nconst first = reduce.toFirst(medals);\n// gold\n```\n\n### To First And Last\nReduces iterable to its first and last elements.\n\n```\nfunction toFirstAndLast\u003cT\u003e(data: Iterable\u003cT\u003e | Iterator\u003cT\u003e): [T, T]\n```\n\nThrows `LengthException` if collection is empty.\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst medals = ['gold', 'silver', 'bronze'];\n\nconst result = reduce.toFirstAndLast(medals);\n// [gold, bronze]\n```\n\n### To Last\nReduces iterable to its last element.\n\n```\nfunction toLast\u003cT\u003e(data: Iterable\u003cT\u003e | Iterator\u003cT\u003e): T\n```\n\nThrows `LengthException` if collection is empty.\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst medals = ['gold', 'silver', 'bronze'];\n\nconst first = reduce.toFirst(medals);\n// bronze\n```\n\n### To Max\nReduces to the max value.\n\n```\nfunction toMax\u003cTValue\u003e(\n  data: Iterable\u003cTValue\u003e|Iterator\u003cTValue\u003e,\n  compareBy?: (datum: TValue) =\u003e Comparable,\n): TValue|undefined\n```\n\n- Optional callable param `compareBy` must return comparable value.\n- If `compareBy` is not provided then items of given collection must be comparable.\n- Returns `undefined` if collection is empty.\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst numbers = [5, 3, 1, 2, 4];\n\nconst result = reduce.toMax(numbers);\n// 1\n\nconst movieRatings = [\n  {\n    title: 'The Matrix',\n    rating: 4.7,\n  },\n  {\n    title: 'The Matrix Reloaded',\n    rating: 4.3,\n  },\n  {\n    title: 'The Matrix Revolutions',\n    rating: 3.9,\n  },\n  {\n    title: 'The Matrix Resurrections',\n    rating: 2.5,\n  },\n];\nconst compareBy = (movie) =\u003e movie.rating;\n\nconst lowestRatedMovie = reduce.toMin(movieRatings, compareBy);\n// {\n//   title: 'The Matrix',\n//   rating: 4.7,\n// }\n```\n\n### To Min\nReduces to the min value.\n\n```\nfunction toMin\u003cTValue\u003e(\n  data: Iterable\u003cTValue\u003e|Iterator\u003cTValue\u003e,\n  compareBy?: (datum: TValue) =\u003e Comparable,\n): TValue|undefined\n```\n\n- Optional callable param `compareBy` must return comparable value.\n- If `compareBy` is not provided then items of given collection must be comparable.\n- Returns `undefined` if collection is empty.\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst numbers = [5, 3, 1, 2, 4];\n\nconst result = reduce.toMin(numbers);\n// 1\n\nconst movieRatings = [\n  {\n    title: 'The Matrix',\n    rating: 4.7,\n  },\n  {\n    title: 'The Matrix Reloaded',\n    rating: 4.3,\n  },\n  {\n    title: 'The Matrix Revolutions',\n    rating: 3.9,\n  },\n  {\n    title: 'The Matrix Resurrections',\n    rating: 2.5,\n  },\n];\nconst compareBy = (movie) =\u003e movie.rating;\n\nconst lowestRatedMovie = reduce.toMin(movieRatings, compareBy);\n// {\n//   title: 'The Matrix Resurrections',\n//   rating: 2.5,\n// }\n```\n\n### To Min Max\nReduces collection to its lower and upper bounds.\n\n```\nfunction toMinMax\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  compareBy?: (item: T) =\u003e Comparable\n): [T?, T?]\n```\n\n- Optional callable param `compareBy` must return comparable value.\n- If `compareBy` is not provided then items of given collection must be comparable.\n- Returns `[undefined, undefined]` if collection is empty.\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst numbers = [5, 3, 1, 2, 4];\n\nconst result = reduce.toMinMax(numbers);\n// [1, 5]\n\nconst movieRatings = [\n  {\n    title: 'The Matrix',\n    rating: 4.7,\n  },\n  {\n    title: 'The Matrix Reloaded',\n    rating: 4.3,\n  },\n  {\n    title: 'The Matrix Revolutions',\n    rating: 3.9,\n  },\n  {\n    title: 'The Matrix Resurrections',\n    rating: 2.5,\n  },\n];\nconst compareBy = (movie) =\u003e movie.rating;\n\nconst lowestRatedMovie = reduce.toMin(movieRatings, compareBy);\n// [{\n//   title: 'The Matrix Resurrections',\n//   rating: 2.5,\n// },\n// {\n//   title: 'The Matrix',\n//   rating: 4.7,\n// }]\n```\n\n### To Product\nReduces to the product of its elements.\n\nReturns `undefined` if collection is empty.\n\n```\nfunction toProduct(data: Iterable\u003cnumber\u003e|Iterator\u003cnumber\u003e): number|undefined\n```\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst primeFactors = [5, 2, 2];\n\nconst number = reduce.toProduct(primeFactors);\n// 20\n```\n\n### To Range\nReduces given collection to its range (difference between max and min).\n\n```\nfunction toRange(numbers: Iterable\u003cNumeric\u003e | Iterator\u003cNumeric\u003e): number\n```\n\nReturns `0` if iterable source is empty.\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst grades = [100, 90, 80, 85, 95];\n\nconst range = reduce.toRange(numbers);\n// 20\n```\n\n### To Sum\nReduces to the sum of its elements.\n\n```\nfunction toSum(data: Iterable\u003cnumber\u003e|Iterator\u003cnumber\u003e): number\n```\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst parts = [10, 20, 30];\n\nconst sum = reduce.toSum(parts);\n// 60\n```\n\n### To Value\nReduce elements to a single value using reducer function.\n\n```\nfunction toValue\u003cTInput, TOutput\u003e(\n  data: Iterable\u003cTInput\u003e | Iterator\u003cTInput\u003e,\n  reducer: (carry: TOutput, datum: TInput) =\u003e TOutput,\n  initialValue?: TOutput\n): TOutput\n```\n\n```typescript\nimport { reduce } from 'itertools-ts';\n\nconst input = [1, 2, 3, 4, 5];\nconst sum = (carry, item) =\u003e carry + item;\n\nconst result = reduce.toValue(input, sum, 0);\n// 15\n```\n\n## Set and multiset\n### Distinct\nFilter out elements from the iterable only returning distinct elements.\n\n```\nfunction* distinct\u003cT\u003e(\n  data: Iterable\u003cT\u003e|Iterator\u003cT\u003e,\n  compareBy?: (datum: T) =\u003e Comparable\n): Iterable\u003cT\u003e\n```\n\nAlways treats different instances of objects and arrays as unequal.\n\n```typescript\nimport { set } from 'itertools-ts';\n\nconst chessSet = ['rook', 'rook', 'knight', 'knight', 'bishop', 'bishop', 'king', 'queen', 'pawn', 'pawn'];\n\nfor (const chessPiece of set.distinct(chessSet)) {\n  console.log(chessPiece);\n}\n// rook, knight, bishop, king, queen, pawn\n\n\nconst users = [\n  { 'name': 'John', 'id': 1 },\n  { 'name': 'Mary', 'id': 2 },\n  { 'name': 'Mary', 'id': 3 },\n  { 'name': 'John', 'id': 4 },\n  { 'name': 'Jane', 'id': 5 },\n];\n\nfor (const user of set.distinct(users, (item) =\u003e item['name'])) {\n  console.log(user);\n}\n// { 'name': 'John', 'id': 1 }, { 'name': 'Mary', 'id': 2 }, { 'name': 'Jane', 'id': 5 }\n```\n\n### Intersection\nIterates intersection of iterables.\n\n```\nfunction* intersection\u003cT\u003e(...iterables: Array\u003cIterable\u003cT\u003e | Iterator\u003cT\u003e\u003e): Iterable\u003cT\u003e\n```\n\n* Always treats different instances of objects and arrays as unequal.\n* If input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) intersection rules apply.\n\n```typescript\nimport { set } from 'itertools-ts';\n\nconst chessPieces = ['rook', 'knight', 'bishop', 'queen', 'king', 'pawn'];\nconst shogiPieces = ['rook', 'knight', 'bishop', 'king', 'pawn', 'lance', 'gold general', 'silver general'];\n\nfor (const commonPiece of set.intersection(chessPieces, shogiPieces)) {\n    console.log(commonPiece);\n}\n// rook, knight, bishop, king, pawn\n```\n\n### Partial Intersection\nIterates [M-partial intersection](https://github.com/Smoren/partial-intersection-php) of iterables.\n\n```\nfunction* partialIntersection\u003cT\u003e(\n  minIntersectionCount: number,\n  ...iterables: Array\u003cIterable\u003cT\u003e | Iterator\u003cT\u003e\u003e\n): Iterable\u003cT\u003e\n```\n\n* Always treats different instances of objects and arrays as unequal.\n* If input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) intersection rules apply.\n\n```typescript\nimport { set } from 'itertools-ts';\n\nconst staticallyTyped    = ['c++', 'java', 'c#', 'go', 'haskell'];\nconst dynamicallyTyped   = ['php', 'python', 'javascript', 'typescript'];\nconst supportsInterfaces = ['php', 'java', 'c#', 'typescript'];\n\nfor (const language of set.partialIntersection(2, staticallyTyped, dynamicallyTyped, supportsInterfaces)) {\n  console.log(language);\n}\n// c++, java, c#, go, php\n```\n\n### Symmetric difference\nIterates the symmetric difference of iterables.\n\n```\nfunction* symmetricDifference\u003cT\u003e(\n  ...iterables: Array\u003cIterable\u003cT\u003e | Iterator\u003cT\u003e\u003e\n): Iterable\u003cT\u003e\n```\n\n* Always treats different instances of objects and arrays as unequal.\n* If input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) difference rules apply.\n\n```typescript\nimport { set } from 'itertools-ts';\n\nconst a = [2, 3, 4, 7];\nconst b = [2, 3, 5, 8];\nconst c = [2, 3, 6, 9];\n\nfor (const item of set.symmetricDifference(a, b, c)) {\n  console.log(item);\n}\n// 4, 5, 6, 7, 8, 9\n```\n\n### Union\nIterates the union of iterables.\n\n```\nfunction* union\u003cT\u003e(...iterables: Array\u003cIterable\u003cT\u003e | Iterator\u003cT\u003e\u003e): Iterable\u003cT\u003e\n```\n\n* Always treats different instances of objects and arrays as unequal.\n* If input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) difference rules apply.\n\n```typescript\nimport { set } from 'itertools-ts';\n\nconst a = [1, 2, 3];\nconst b = [2, 3, 4];\nconst c = [3, 4, 5];\n\nfor (const item of set.symmetricDifference(a, b, c)) {\n    console.log(item);\n}\n// 1, 2, 3, 4, 5\n```\n\n## Combinatorics\n### Cartesian Product\nIterates cartesian product of given iterables.\n\n```\nfunction* cartesianProduct\u003cT extends Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e\u003e(\n  ...iterables: T\n): Iterable\u003cZipTuple\u003cT, never\u003e\u003e\n```\n\n```typescript\nimport { combinatorics } from 'itertools-ts';\n\nconst numbers = [1, 2];\nconst letters = ['a', 'b'];\nconst chars = ['!', '?'];\n\nfor (const tuple of combinatorics.cartesianProduct(numbers, letters, chars)) {\n  console.log(tuple);\n}\n/*\n  [1, 'a', '!'],\n  [1, 'a', '?'],\n  [1, 'b', '!'],\n  [1, 'b', '?'],\n  [2, 'a', '!'],\n  [2, 'a', '?'],\n  [2, 'b', '!'],\n  [2, 'b', '?'],\n*/\n```\n\n### Combinations\nIterates all combinations of given iterable.\n\n```\nfunction* combinations\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  length: number\n): Iterable\u003cArray\u003cT\u003e\u003e\n```\n\n```typescript\nimport { combinatorics } from 'itertools-ts';\n\nconst fruits = ['apple', 'banana', 'cherry'];\n\nfor (const combination of combinatorics.combinations(fruits, 2)) {\n  console.log(combination);\n}\n// ['apple', 'banana']\n// ['apple', 'cherry']\n// ['banana', 'cherry']\n```\n\n### Permutations\nIterates all permutations of given iterable.\n\n```\nfunction* permutations\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  length: number\n): Iterable\u003cArray\u003cT\u003e\u003e\n```\n\n```typescript\nimport { combinatorics } from 'itertools-ts';\n\nconst fruits = ['apple', 'banana', 'cherry'];\n\nfor (const permutation of combinatorics.permutations(fruits, 2)) {\n  console.log(permutation);\n}\n// ['apple', 'banana']\n// ['apple', 'cherry']\n// ['banana', 'apple']\n// ['banana', 'cherry']\n// ['cherry', 'apple']\n// ['cherry', 'banana']\n```\n\n## Summary\n### All Match\nReturns true if all elements match the predicate function.\n\n```\nfunction allMatch\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  predicate: (item: T) =\u003e boolean\n): boolean\n```\n\nEmpty collections return true.\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst finalFantasyNumbers = [4, 5, 6];\nconst isOnSuperNintendo   = (ff) =\u003e ff \u003e= 4 \u0026\u0026 ff \u003c= 6;\n\nconst trueResult = summary.allMatch(finalFantasyNumbers, isOnSuperNintendo);\n// true\n\nconst isOnPlaystation = (ff) =\u003e ff \u003e= 7 \u0026\u0026 ff \u003c= 9;\n\nconst falseResult = summary.allMatch(finalFantasyNumbers, isOnPlaystation);\n// false\n```\n\n### All Unique\nReturn true if all elements in given collection are unique.\n\n```\nfunction allUnique(data: Iterable\u003cunknown\u003e | Iterator\u003cunknown\u003e): boolean\n```\n\nEmpty collections return true.\n\nConsiders different instances of data containers to be different, even if they have the same content.\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst uniqueNumbers = [1, 2, 3, 4, 5];\nsummary.allUnique(uniqueNumbers);\n// true\n\nconst notUniqueNumbers = [1, 1, 2, 2, 3];\nsummary.allUnique(notUniqueNumbers);\n// false\n```\n\n### Any Match\nReturns true if any element matches the predicate function.\n\n```\nfunction anyMatch\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  predicate: (item: T) =\u003e boolean\n): boolean\n```\n\nEmpty collections return false.\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst answers          = ['fish', 'towel', 42, \"don't panic\"];\nconst isUltimateAnswer = (a) =\u003e a == 42;\n\nconst trueResult = summary.anyMatch(answers, isUltimateAnswer);\n// true\n```\n\n### Exactly N\nReturns true if exactly n items are true according to a predicate function.\n\n- Predicate is optional.\n- Default predicate is boolean value of each item.\n\n```\nfunction exactlyN\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  n: number,\n  predicate?: (item: T) =\u003e boolean,\n): boolean\n```\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst twoTruthsAndALie = [true, true, false];\nconst n = 2;\n\nconst trueResult = summary.exactlyN(twoTruthsAndALie, n);\n// true\n\nconst ages = [18, 21, 24, 54];\nconst m = 4;\nconst predicate = (age) =\u003e age \u003e= 21;\n\nconst falseResult = Summary::exactlyN(ages, m, predicate);\n// false\n```\n\n### Is Async Iterable\nReturns true if given data is an `AsyncIterable` instance.\n\n```\nfunction isAsyncIterable(input: unknown): boolean\n```\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nsummary.isIterable(input); // false\nsummary.isIterable(input[Symbol.asyncIterator]()) // false\nsummary.isIterable(1); // false\n```\n\n### Is Iterable\nReturns true if given data is an `Iterable` instance.\n\n```\nfunction isIterable(input: unknown): boolean\n```\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nsummary.isIterable(input); // true\nsummary.isIterable(input[Symbol.iterator]()) // false\nsummary.isIterable(1); // false\n```\n\n### Is Iterator\nReturns true if given data is an `Iterator` instance.\n\n```\nfunction isIterator(input: unknown): boolean\n```\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nsummary.isIterator(input[Symbol.iterator]()) // true\nsummary.isIterator(input); // false\nsummary.isIterator(1); // false\n```\n\n### Is Reversed\nReturns true if elements are reverse sorted, otherwise false.\n\n```\nfunction isReversed(data: Iterable\u003cComparable\u003e | Iterator\u003cComparable\u003e): boolean\n```\n\n- Elements must be comparable.\n- Returns true if empty or has only one element.\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst reversedNumbers = [5, 4, 3, 2, 1];\n\nSummary.isReversed(reversedNumbers);\n// true\n\nconst numbers = [1, 4, 3, 2, 1];\n\nSummary.isReversed(numbers);\n// false\n```\n\n### Is Sorted\nReturns true if elements are sorted, otherwise false.\n\n```\nfunction isSorted(data: Iterable\u003cComparable\u003e | Iterator\u003cComparable\u003e): boolean\n```\n\n- Elements must be comparable.\n- Returns true if empty or has only one element.\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst sortedNumbers = [1, 2, 3, 4, 5];\n\nSummary.isSorted(sortedNumbers);\n// true\n\nconst numbers = [3, 2, 3, 4, 5];\n\nSummary.isSorted(numbers);\n// false\n```\n\n### Is String\nReturns true if given data is a string.\n\n```\nfunction isString(input: unknown): boolean\n```\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nsummary.isString('') // true\nsummary.isString('abc') // true\nsummary.isString(String('abc')) // true\nsummary.isString(1); // false\n```\n\n### None Match\nReturns true if no element matches the predicate function.\n\n```\nfunction noneMatch\u003cT\u003e(\n  data: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  predicate: (item: T) =\u003e boolean\n): boolean\n```\n\nEmpty collections return true.\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst grades         = [45, 50, 61, 0];\nconst isPassingGrade = (grade) =\u003e grade \u003e= 70;\n\nconst trueResult = summary.noneMatch(grades, isPassingGrade);\n// true\n```\n\n### Same\nReturns true if all given collections are the same.\n\nFor single collection or empty collections list returns true.\n\n```\nfunction same(...collections: Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e): boolean\n```\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst cocaColaIngredients = ['carbonated water', 'sugar', 'caramel color', 'phosphoric acid'];\nconst pepsiIngredients    = ['carbonated water', 'sugar', 'caramel color', 'phosphoric acid'];\nconst spriteIngredients   = ['carbonated water', 'sugar', 'citric acid', 'lemon lime flavorings'];\n\nconst trueResult = summary.same(cocaColaIngredients, pepsiIngredients);\n// true\n\nconst falseResult = summary.same(cocaColaIngredients, spriteIngredients);\n// false\n```\n\n### Same Count\nReturns true if all given collections have the same lengths.\n\nFor single collection or empty collections list returns true.\n\n```\nfunction same(\n  ...collections: Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e\n): boolean\n```\n\n```typescript\nimport { summary } from \"itertools-ts\";\n\nconst prequels  = ['Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith'];\nconst originals = ['A New Hope', 'Empire Strikes Back', 'Return of the Jedi'];\nconst sequels   = ['The Force Awakens', 'The Last Jedi', 'The Rise of Skywalker'];\n\nconst trueResult = summary.sameCount(prequels, originals, sequels);\n// true\n\nconst batmanMovies = ['Batman Begins', 'The Dark Knight', 'The Dark Knight Rises'];\nconst matrixMovies = ['The Matrix', 'The Matrix Reloaded', 'The Matrix Revolutions', 'The Matrix Resurrections'];\n\nconst falseResult = summary.sameCount(batmanMovies, matrixMovies);\n// false\n```\n\n## Transform\n### Tee\nReturn several independent (duplicated) iterators from a single iterable.\n\n```\nfunction tee\u003cT\u003e(\n  collection: Iterable\u003cT\u003e | Iterator\u003cT\u003e,\n  count: number\n): Array\u003cRelatedIterable\u003cT\u003e\u003e\n```\n\nOnce tee has been called to duplicate iterators, it is advisable to not use the original input iterator any further.\n\nDuplicating iterators can use up memory. Consider if tee is the right solution. For example, arrays and most\niterators can be rewound and reiterated without need for duplication.\n\n```typescript\nimport { transform } from \"itertools-ts\";\n\nconst daysOfWeek = ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun'];\nconst count = 3;\n\nconst [week1, week2, week3] = transform.tee(data, count);\n// Each week contains iterator containing ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun']\n```\n\n### To Array\nReturns `Array` instance of given collection or iterator.\n\n```\nfunction toArray\u003cT\u003e(\n  collection: Iterable\u003cT\u003e|Iterator\u003cT\u003e\n): Array\u003cT\u003e\n```\n\n```typescript\nimport { transform } from \"itertools-ts\";\n\nconst iterator = transform.toIterator([1, 2, 3, 4, 5]);\n\nconst result = transform.toArray(iterator);\n// [1, 2, 3, 4, 5]\n```\n\n### To Async Iterable\nReturns `AsyncIterable` instance of given collection, record or iterator (sync or async).\n\nThrows `InvalidArgumentError` if given data is not a collection or an iterator.\n\n```\nfunction toAsyncIterable\u003cT\u003e(\n  collection:\n    | Iterable\u003cT\u003e\n    | Iterator\u003cT\u003e\n    | AsyncIterable\u003cT\u003e\n    | AsyncIterator\u003cT\u003e\n    | Record\u003cRecordKey, unknown\u003e\n): AsyncIterable\u003cT\u003e\n```\n\n```typescript\nimport { transform } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nconst result = transform.toAsyncIterable(input);\n// AsyncIterable\u003c[1, 2, 3, 4, 5]\u003e\n```\n\n### To Async Iterator\nReturns `AsyncIterator` instance of given collection or iterator.\n\nThrows `InvalidArgumentError` if given data is not a collection or an iterator.\n\n```\nfunction toAsyncIterator\u003cT\u003e(\n  collection: Iterable\u003cT\u003e | Iterator\u003cT\u003e | AsyncIterable\u003cT\u003e | AsyncIterator\u003cT\u003e\n): AsyncIterator\u003cT\u003e\n```\n\n```typescript\nimport { transform } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nconst result = transform.toAsyncIterator(input);\nconsole.log(result.next !== undefined);\n// true\n```\n\n### To Iterable\nReturns `Iterable` instance of given collection, record or iterator.\n\nThrows `InvalidArgumentError` if given data is not a collection or an iterator.\n\n```\nfunction toIterable\u003cT\u003e(\n  collection: Iterable\u003cT\u003e|Iterator\u003cT\u003e|Record\u003cstring|number|symbol, unknown\u003e\n): Iterable\u003cT\u003e\n```\n\n```typescript\nimport { transform } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nconst result = transform.toIterable(input);\n// [1, 2, 3, 4, 5]\n```\n\n### To Iterator\nReturns `Iterator` instance of given collection or iterator.\n\nThrows `InvalidArgumentError` if given data is not a collection or an iterator.\n\n```\nfunction toIterator\u003cT\u003e(collection: Iterable\u003cT\u003e|Iterator\u003cT\u003e): Iterator\u003cT\u003e\n```\n\n```typescript\nimport { transform } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nconst result = transform.toIterator(input);\nconsole.log(result.next !== undefined);\n// true\n```\n\n### To Map\nConverts given iterable of key-value pairs to Map.\n\n```\nfunction toMap\u003cTKey, TValue\u003e(\n  pairs: Iterable\u003c[TKey, TValue]\u003e | Iterator\u003c[TKey, TValue]\u003e | Record\u003cstring|number|symbol, unknown\u003e\n): Map\u003cTKey, TValue\u003e\n```\n\n```typescript\nimport { transform } from \"itertools-ts\";\n\nconst input = [['a', 1], ['b', 2], ['c', 3]];\n\nconst result = transform.toMap(input);\n// Map([['a', 1], ['b', 2], ['c', 3]])\n```\n\n### To Set\nConverts given iterable to Set.\n\n```\nfunction toSet\u003cT\u003e(\n  collection: Iterable\u003cT\u003e | Iterator\u003cT\u003e\n): Set\u003cT\u003e\n```\n\n```typescript\nimport { transform } from \"itertools-ts\";\n\nconst input = [1, 1, 2, 2, 3, 3];\n\nconst result = transform.toSet(input);\n// Set([1, 2, 3])\n```\n\n## Stream and Async Stream\nStreams provide a fluent interface to transform arrays and iterables (sync or async) through a pipeline of operations.\n\nStreams are made up of:\n\n1. One stream source factory method to create the stream.\n2. Zero or more stream operators that transform the stream to a new stream.\n3. Terminal operation of either:\n   * Stream terminal operation to transform the stream to a value or data structure.\n     ```typescript\n     const result1 = Stream.of([1, 1, 2, 2, 3, 4, 5])\n       .distinct()             // [1, 2, 3, 4, 5]\n       .map((x) =\u003e x**2)       // [1, 4, 9, 16, 25]\n       .filter((x) =\u003e x \u003c 10)  // [1, 4, 9]\n       .toSum();               // 14\n\n     // Async example\n     const result2 = await AsyncStream.of([1, 1, 2, 2, 3, 4, 5].map((x) =\u003e Promise.resolve(x)))\n       .distinct()             // [1, 2, 3, 4, 5]\n       .map((x) =\u003e x**2)       // [1, 4, 9, 16, 25]\n       .filter((x) =\u003e x \u003c 10)  // [1, 4, 9]\n       .toSum();               // 14\n     ```\n   * The stream is iterated via a `for` loop.\n     ```typescript\n     const result1 = Stream.of([1, 1, 2, 2, 3, 4, 5])\n       .distinct()             // [1, 2, 3, 4, 5]\n       .map((x) =\u003e x**2)       // [1, 4, 9, 16, 25]\n       .filter((x) =\u003e x \u003c 10); // [1, 4, 9]\n\n     for (const item of result1) {\n       // 1, 4, 9\n     }\n\n     // Async example\n     const result2 = AsyncStream.of([1, 1, 2, 2, 3, 4, 5].map((x) =\u003e Promise.resolve(x)))\n       .distinct()             // [1, 2, 3, 4, 5]\n       .map((x) =\u003e x**2)       // [1, 4, 9, 16, 25]\n       .filter((x) =\u003e x \u003c 10); // [1, 4, 9]\n\n     for await (const item of result2) {\n       // 1, 4, 9\n     }\n     ```\n\n### Stream Sources\n#### Of\nCreates stream from an iterable.\n\n```\nStream.of\u003cT\u003e(data: Iterable\u003cT\u003e | Iterator\u003cT\u003e): Stream\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst iterable = [1, 2, 3];\n\nconst result = Stream.of(iterable)\n  .chainWith([4, 5, 6], [7, 8, 9])\n  .zipEqualWith([1, 2, 3, 4, 5, 6, 7, 8, 9])\n  .toArray();\n// [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5], [6, 6], [7, 7], [8, 8], [9, 9]]\n```\n\n#### Of Empty\nCreates stream of nothing.\n\n```\nStream.ofEmpty(): Stream\u003cnever\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst result = Stream.ofEmpty()\n  .chainWith([1, 2, 3])\n  .toArray();\n// [1, 2, 3]\n```\n\n#### Of Count\nCreate an infinite count stream.\n\n```\nStream.ofCount(start: number = 1, step: number = 1): Stream\u003cnumber\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst result = Stream.ofCount(0, 10)\n  .limit(5)\n  .toArray();\n// [0, 10, 20, 30, 40]\n```\n\n#### Of Cycle\nCreate an infinite cycle stream.\n\n```\nStream.ofCycle\u003cT\u003e(iterable: Iterable\u003cT\u003e | Iterator\u003cT\u003e): Stream\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst result = Stream.ofCycle([1, 2, 3])\n  .limit(7)\n  .toArray();\n// [1, 2, 3, 1, 2, 3, 1]\n```\n\n#### Of Repeat\nCreate an infinite stream repeating given item.\n\n```\nStream.ofRepeat\u003cT\u003e(item: T): Stream\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst result = Stream.ofRepeat('bla')\n  .limit(5)\n  .toArray();\n// [bla, bla, bla, bla, bla]\n```\n\n### Stream Operations\n#### Cartesian Product With\nIterate cartesian product of iterable source with another iterable collections.\n\n```\nStream\u003cT\u003e.cartesianProductWith\u003cU extends Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e\u003e(\n  ...iterables: U\n): Stream\u003cZipTuple\u003c[Iterable\u003cT\u003e, ...U], never\u003e\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst numbers = [1, 2];\n\nconst result = Stream.of(numbers)\n  .cartesianProductWith(['a', 'b'], ['!', '?'])\n  .toArray();\n/*\n[\n  [1, 'a', '!'],\n  [1, 'a', '?'],\n  [1, 'b', '!'],\n  [1, 'b', '?'],\n  [2, 'a', '!'],\n  [2, 'a', '?'],\n  [2, 'b', '!'],\n  [2, 'b', '?'],\n]\n*/\n```\n\n#### Chain With\nReturn a stream chaining additional sources together into a single consecutive stream.\n\n```\nStream\u003cT\u003e.chainWith(...iterables: Array\u003cIterable\u003cT\u003e | Iterator\u003cT\u003e\u003e): Stream\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 3];\n\nconst result = Stream.of(input)\n  .chainWith([4, 5, 6])\n  .chainWith([7, 8, 9])\n  .toArray();\n// [1, 2, 3, 4, 5, 6, 7, 8, 9]\n```\n\n#### Chunkwise\nReturn a stream consisting of chunks of elements from the stream.\n\n```\nStream\u003cT\u003e.chunkwise(chunkSize: number): Stream\u003cArray\u003cT\u003e\u003e\n```\n\nChunk size must be at least 1.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst friends = ['Ross', 'Rachel', 'Chandler', 'Monica', 'Joey'];\n\nconst result = Stream.of(friends)\n  .chunkwise(2)\n  .toArray();\n// [['Ross', 'Rachel'], ['Chandler', 'Monica'], ['Joey']]\n```\n\n#### Chunkwise Overlap\nReturn a stream consisting of overlapping chunks of elements from the stream.\n\n```\nStream\u003cT\u003e.chunkwiseOverlap(\n  chunkSize: number,\n  overlapSize: number,\n  includeIncompleteTail = true\n): Stream\u003cArray\u003cT\u003e\u003e\n```\n\n* Chunk size must be at least 1.\n* Overlap size must be less than chunk size.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];\n\nconst result = Stream.of(numbers)\n  .chunkwiseOverlap(3, 1)\n  .toArray()\n// [[1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]]\n```\n\n#### Combinations\nReturn a stream with combinations of the stream iterable.\n\n```\nStream\u003cT\u003e.combinations(length: number): Stream\u003cArray\u003cT\u003e\u003e\n```\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst fruits = ['apple', 'banana', 'cherry'];\n\nconst result = Stream.of(fruits)\n  .combinations(2)\n  .toArray();\n/*\n[\n  ['apple', 'banana'],\n  ['apple', 'cherry'],\n  ['banana', 'cherry'],\n]\n*/\n```\n\n#### Compress\nCompress to a new stream by filtering out data that is not selected.\n\n```\nStream\u003cT\u003e.compress(\n  selectors: Iterable\u003cnumber | boolean\u003e | Iterator\u003cnumber | boolean\u003e\n): Stream\u003cT\u003e\n```\n\nSelectors indicate which data. True value selects item. False value filters out data.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 3];\n\nconst result = Stream.of(input)\n  .compress([0, 1, 1])\n  .toArray();\n// [2, 3]\n```\n\n#### Distinct\nReturn a stream filtering out elements from the stream only returning distinct elements.\n\n```\nStream\u003cT\u003e.distinct(compareBy?: (datum: T) =\u003e Comparable): Stream\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 1, 2, 3, 3, '1', '1', '2', '3'];\nconst numbers = Stream.of(input)\n  .distinct()\n  .toArray();\n// [1, 2, 3, '1', '2', '3']\n\nconst users = [\n  { 'name': 'John', 'id': 1 },\n  { 'name': 'Mary', 'id': 2 },\n  { 'name': 'Mary', 'id': 3 },\n  { 'name': 'John', 'id': 4 },\n  { 'name': 'Jane', 'id': 5 },\n];\nconst result = Stream.of(input)\n  .distinct((item) =\u003e item['name'])\n  .toArray();\n/*\n[\n  { 'name': 'John', 'id': 1 },\n  { 'name': 'Mary', 'id': 2 },\n  { 'name': 'Jane', 'id': 5 },\n]\n*/\n```\n\n#### Drop While\nDrop elements from the stream while the predicate function is true.\n\n```\nStream\u003cT\u003e.dropWhile(predicate: (item: T) =\u003e boolean): Stream\u003cT\u003e\n```\n\nOnce the predicate function returns false once, all remaining elements are returned.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5]\n\nconst result = Stream.of(input)\n  .dropWhile((value) =\u003e value \u003c 3)\n  .toArray();\n// [3, 4, 5]\n```\n\n#### Enumerate\nEnumerates elements of the stream.\n\n```\nStream\u003cT\u003e.enumerate(): Stream\u003c[number, T]\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = ['a', 'b', 'c', 'd', 'e'];\nconst stream = Stream.of(input)\n  .enumerate()\n  .toArray();\n// [[0, 'a'], [1, 'b'], [2, 'c'], [3, 'd'], [4, 'e']]\n```\n\n#### Filter\nFilter out elements from the stream only keeping elements where there predicate function is true.\n\n```\nStream\u003cT\u003e.filter(predicate: (item: T) =\u003e boolean): Stream\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, -1, 2, -2, 3, -3];\n\nconst result = Stream.of(input)\n  .filter((value) =\u003e value \u003e 0)\n  .toArray();\n// [1, 2, 3]\n```\n\n#### Flat Map\nMap a function onto the elements of the stream and flatten the results.\n\n```\nStream\u003cT\u003e.flatMap\u003cU\u003e(mapper: FlatMapper\u003cT, U\u003e): Stream\u003cU\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst data = [1, 2, 3, 4, 5];\nconst mapper = (item) =\u003e (item % 2 === 0) ? [item, item] : item;\n\nconst result = Stream.of(data)\n  .flatMap(mapper)\n  .toArray();\n// [1, 2, 2, 3, 4, 4, 5]\n```\n\n#### Flatten\nFlatten a multidimensional stream.\n\n```\nStream\u003cT\u003e.flatten(dimensions: number = Infinity): Stream\u003cunknown\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst data = [1, [2, 3], [4, 5]];\n\nconst result = Stream.of(data)\n  .flatten()\n  .toArray();\n// [1, 2, 3, 4, 5]\n```\n\n#### Intersection With\nReturn a stream intersecting the stream with the input iterables.\n\n```\nStream\u003cT\u003e.intersectionWith(...iterables: Array\u003cIterable\u003cT\u003e | Iterator\u003cT\u003e\u003e): Stream\u003cT\u003e\n```\n\n* Always treats different instances of objects and arrays as unequal.\n* If input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) intersection rules apply.\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst chessPieces = ['rook', 'knight', 'bishop', 'queen', 'king', 'pawn'];\nconst shogiPieces = ['rook', 'knight', 'bishop', 'king', 'pawn', 'lance', 'gold general', 'silver general'];\n\nconst result = Stream.of(chessPieces)\n  .intersectionWith(shogiPieces)\n  .toArray();\n// [rook, knight, bishop, king, pawn]\n```\n\n#### Group By\nGroup stream data by a common data element.\n\nIterate pairs of group name and collection of grouped items.\n\n```\nStream\u003cT\u003e.groupBy\u003c\n  TItemKeyFunction extends ((item: T) =\u003e string) | undefined,\n  TResultItem extends TItemKeyFunction extends undefined ? [string, Array\u003cT\u003e] : [string, Record\u003cstring, T\u003e]\n\u003e(\n  groupKeyFunction: (item: T) =\u003e string,\n  itemKeyFunction?: TItemKeyFunction\n): Stream\u003cTResultItem\u003e\n```\n\n* The `groupKeyFunction` determines the key to group elements by.\n* The optional `itemKeyFunction` allows custom indexes within each group member.\n* Collection of grouped items may be an array or an object (depends on presence of `itemKeyFunction` param).\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst cartoonCharacters = [\n    ['Garfield', 'cat'],\n    ['Tom', 'cat'],\n    ['Felix', 'cat'],\n    ['Heathcliff', 'cat'],\n    ['Snoopy', 'dog'],\n    ['Scooby-Doo', 'dog'],\n    ['Odie', 'dog'],\n    ['Donald', 'duck'],\n    ['Daffy', 'duck'],\n];\n\nconst result = Stream.of(cartoonCharacters)\n  .groupBy((x) =\u003e x[1])\n  .toArray();\n/*\n[\n  ['cat', [\n    ['Garfield', 'cat'],\n    ['Tom', 'cat'],\n    ['Felix', 'cat'],\n    ['Heathcliff', 'cat'],\n  ]],\n  ['dog', [\n    ['Snoopy', 'dog'],\n    ['Scooby-Doo', 'dog'],\n    ['Odie', 'dog'],\n  ]],\n  ['duck', [\n    ['Donald', 'duck'],\n    ['Daffy', 'duck'],\n  ]],\n]\n*/\n```\n\n#### Keys\nIterate keys of key-value pairs.\n\n```\nStream\u003cT\u003e.keys(): Stream\u003cT extends [infer TKey, infer _] ? TKey : never\u003e\n```\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst dict = new Map([['a', 1], ['b', 2], ['c', 3]]);\n\nconst result = Stream.of(dict)\n  .keys()\n  .toArray();\n// ['a', 'b', 'c']\n```\n\n#### Limit\nReturn a stream up to a limit.\n\nStops even if more data available if limit reached.\n\n```\nStream\u003cT\u003e.limit(count: number): Stream\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst matrixMovies = ['The Matrix', 'The Matrix Reloaded', 'The Matrix Revolutions', 'The Matrix Resurrections'];\nconst limit = 1;\n\nconst goodMovies = Stream.of(matrixMovies)\n  .limit(limit)\n  .toArray();\n// ['The Matrix'] (and nothing else)\n```\n\n#### Map\nReturn a stream containing the result of mapping a function onto each element of the stream.\n\n```\nStream\u003cT\u003e.map\u003cU\u003e(mapper: (datum: T) =\u003e U): Stream\u003cU\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst grades = [100, 95, 98, 89, 100];\n\nconst result = Stream.of(grades)\n  .map((grade) =\u003e grade === 100 ? 'A' : 'F')\n  .toArray();\n// [A, F, F, F, A]\n```\n\n#### Pairwise\nReturn a stream consisting of pairs of elements from the stream.\n\n```\nStream\u003cT\u003e.pairwise(): Stream\u003c[T, T]\u003e\n```\n\nReturns empty stream if given collection contains less than 2 elements.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nconst stream = Stream.of(input)\n  .pairwise()\n  .toArray();\n// [[1, 2], [2, 3], [3, 4], [4, 5]]\n```\n\n#### Partial Intersection With\nReturn a stream [partially intersecting](https://github.com/Smoren/partial-intersection-php) the stream with the input iterables.\n\n```\nStream\u003cT\u003e.partialIntersectionWith(\n  minIntersectionCount: number,\n  ...iterables: Array\u003cIterable\u003cT\u003e | Iterator\u003cT\u003e\u003e\n): Stream\u003cT\u003e\n```\n\n* Always treats different instances of objects and arrays as unequal.\n* If input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) intersection rules apply.\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst staticallyTyped    = ['c++', 'java', 'c#', 'go', 'haskell'];\nconst dynamicallyTyped   = ['php', 'python', 'javascript', 'typescript'];\nconst supportsInterfaces = ['php', 'java', 'c#', 'typescript'];\n\nconst result = Stream.of(staticallyTyped)\n  .partialIntersectionWith(2, dynamicallyTyped, supportsInterfaces)\n  .toArray();\n// ['c++', 'java', 'c#', 'go', 'php']\n```\n\n#### Permutations\nReturn a stream with permutations of the stream iterable.\n\n```\nStream\u003cT\u003e.permutations(length: number): Stream\u003cArray\u003cT\u003e\u003e\n```\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst fruits = ['apple', 'banana', 'cherry'];\n\nconst result = Stream.of(fruits)\n  .permutations(2)\n  .toArray();\n/*\n[\n  ['apple', 'banana'],\n  ['apple', 'cherry'],\n  ['banana', 'apple'],\n  ['banana', 'cherry'],\n  ['cherry', 'apple'],\n  ['cherry', 'banana']\n]\n*/\n```\n\n#### Running Average\nReturn a stream accumulating the running average (mean) over the stream.\n\n```\nStream\u003cT\u003e.runningAverage(initialValue?: number): Stream\u003cnumber\u003e\n```\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst input = [1, 3, 5];\n\nconst result = Stream.of(input)\n  .runningAverage()\n  .toArray();\n// [1, 2, 3]\n```\n\n#### Running Difference\nReturn a stream accumulating the running difference over the stream.\n\n```\nStream\u003cT\u003e.runningDifference(initialValue?: number): Stream\u003cnumber\u003e\n```\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst input = [1, 2, 3, 4, 5];\n\nconst result = Stream.of(input)\n  .runningDifference()\n  .toArray();\n// [-1, -3, -6, -10, -15]\n```\n\n#### Running Max\nReturn a stream accumulating the running max over the stream.\n\n```\nStream\u003cT\u003e.runningMax(initialValue?: number): Stream\u003cnumber\u003e\n```\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst input = [1, -1, 2, -2, 3, -3];\n\nconst result = Stream.of(input)\n  .runningMax()\n  .toArray();\n// [1, 1, 2, 2, 3, 3]\n```\n\n#### Running Min\nReturn a stream accumulating the running min over the stream.\n\n```\nStream\u003cT\u003e.runningMin(initialValue?: number): Stream\u003cnumber\u003e\n```\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst input = [1, -1, 2, -2, 3, -3];\n\nconst result = Stream.of(input)\n  .runningMin()\n  .toArray();\n// [1, -1, -1, -2, -2, -3]\n```\n\n#### Running Product\nReturn a stream accumulating the running product over the stream.\n\n```\nStream\u003cT\u003e.runningProduct(initialValue?: number): Stream\u003cnumber\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nconst result = Stream.of(input)\n  .runningProduct()\n  .toArray();\n// [1, 2, 6, 24, 120]\n```\n\n#### Running Total\nReturn a stream accumulating the running total over the stream.\n\n```\nStream\u003cT\u003e.runningTotal(initialValue?: number): Stream\u003cnumber\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nconst result = Stream.of(input)\n  .runningTotal()\n  .toArray();\n// [1, 3, 6, 10, 15]\n```\n\n#### Skip\nSkip some elements of the stream.\n\n```\nStream\u003cT\u003e.skip(count: number, offset = 0): Stream\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst movies = [\n    'The Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith',\n    'A New Hope', 'The Empire Strikes Back', 'Return of the Jedi',\n    'The Force Awakens', 'The Last Jedi', 'The Rise of Skywalker'\n];\n\nconst onlyTheBest = Stream.of(movies)\n  .skip(3)\n  .skip(3, 3)\n  .toArray();\n// ['A New Hope', 'The Empire Strikes Back', 'Return of the Jedi']\n```\n\n#### Slice\nExtract a slice of the stream.\n\n```\nStream\u003cT\u003e.slice(start: number = 0, count?: number, step: number = 1): Stream\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst olympics = [1992, 1994, 1996, 1998, 2000, 2002, 2004, 2006, 2008, 2010, 2012, 2014, 2016, 2018, 2020, 2022];\n\nconst summerOlympics = Stream.of(olympics)\n  .slice(0, 8, 2)\n  .toArray();\n// [1992, 1996, 2000, 2004, 2008, 2012, 2016, 2020]\n```\n\n#### Sort\nSorts the stream.\n\n```\nStream\u003cT\u003e.sort(comparator?: Comparator\u003cT\u003e): Stream\u003cT\u003e\n```\n\nIf comparator is not provided, the elements of the iterable source must be comparable.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [3, 4, 5, 9, 8, 7, 1, 6, 2];\n\nconst result = Stream.of(input)\n  .sort()\n  .toArray();\n// [1, 2, 3, 4, 5, 6, 7, 8, 9]\n```\n\n#### Symmetric difference With\nReturn a stream of the symmetric difference of the stream and the given iterables.\n\n```\nStream\u003cT\u003e.symmetricDifferenceWith(...iterables: Array\u003cIterable\u003cT\u003e | Iterator\u003cT\u003e\u003e): Stream\u003cT\u003e\n```\n\n* Always treats different instances of objects and arrays as unequal.\n* If input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) difference rules apply.\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst a = [2, 3, 4, 7];\nconst b = [2, 3, 5, 8];\nconst c = [2, 3, 6, 9];\n\nconst result = Stream.of(a)\n  .symmetricDifferenceWith(b, c)\n  .toArray();\n// [4, 5, 6, 7, 8, 9]\n```\n\n#### Take While\nKeep elements from the stream as long as the predicate is true.\n\n```\nStream\u003cT\u003e.takeWhile(predicate: (item: T) =\u003e boolean): Stream\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst input = [1, -1, 2, -2, 3, -3];\n\nconst result = Stream.of(input)\n  .takeWhile((value) =\u003e Math.abs(value) \u003c 3)\n  .toArray();\n// [1, -1, 2, -2]\n```\n\n#### Union With\nReturn a stream of union of the stream with the input iterables.\n\n```\nStream\u003cT\u003e.unionWith(...iterables: Array\u003cIterable\u003cT\u003e | Iterator\u003cT\u003e\u003e): Stream\u003cT\u003e\n```\n\n* Always treats different instances of objects and arrays as unequal.\n* If input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) difference rules apply.\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst a = [1, 2, 3];\nconst b = [2, 3, 4];\nconst c = [3, 4, 5];\n\nconst result = Stream.of(a)\n  .unionWith(b, c)\n  .toArray();\n// [1, 2, 3, 4, 5]\n```\n\n#### Values\nIterate keys of key-value pairs.\n\n```\nStream\u003cT\u003e.values(): Stream\u003cT extends [infer _, infer TValue] ? TValue : never\u003e\n```\n\n```typescript\nimport { Stream } from 'itertools-ts';\n\nconst dict = new Map([['a', 1], ['b', 2], ['c', 3]]);\n\nconst result = Stream.of(dict)\n  .values()\n  .toArray();\n// [1, 2, 3]\n```\n\n#### Zip With\nReturn a stream consisting of multiple iterable collections streamed simultaneously.\n\n```\nStream\u003cT\u003e.zipWith\u003c\n  U extends Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e\n\u003e(...iterables: U): Stream\u003cZipTuple\u003c[Iterable\u003cT\u003e, ...U], never\u003e\u003e\n```\n\nFor uneven lengths, iterations stops when the shortest iterable is exhausted.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 3];\n\nconst stream = Stream.of(input)\n  .zipWith([4, 5, 6])\n  .toArray();\n// [[1, 4], [2, 5], [3, 6]]\n```\n\n#### Zip Equal With\nReturn a stream consisting of multiple iterable collections of equal lengths streamed simultaneously.\n\n```\nStream\u003cT\u003e.zipEqualWith\u003c\n  U extends Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e\n\u003e(...iterables: U): Stream\u003cZipTuple\u003c[Iterable\u003cT\u003e, ...U], never\u003e\u003e\n```\n\nWorks like `Stream.zipWith()` method but throws `LengthException` if lengths not equal,\ni.e., at least one iterator ends before the others.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 3];\n\nconst stream = Stream.of(input)\n  .zipEqualWith([4, 5, 6]);\n\nfor (const zipped of stream) {\n    // [1, 4], [2, 5], [3, 6]\n}\n```\n\n#### Zip Filled With\nReturn a stream consisting of multiple iterable collections streamed simultaneously.\n\n```\nStream\u003cT\u003e.zipFilledWith\u003c\n  U extends Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e,\n  F\n\u003e(filler: F, ...iterables: U): Stream\u003cZipTuple\u003c[Iterable\u003cT\u003e, ...U], F\u003e\u003e\n```\n\n* Iteration continues until the longest iterable is exhausted.\n* For uneven lengths, the exhausted iterables will produce `filler` value for the remaining iterations.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nconst stream = Stream.of(input)\n  .zipFilledWith('filler', [4, 5, 6]);\n\nfor (const zipped of stream) {\n  // [1, 4], [2, 5], [3, 6], [4, 'filler'], [5, 'filler']\n}\n```\n\n#### Zip Longest With\nReturn a stream consisting of multiple iterable collections streamed simultaneously.\n\n```\nStream\u003cT\u003e.zipLongestWith\u003c\n  U extends Array\u003cIterable\u003cunknown\u003e | Iterator\u003cunknown\u003e\u003e\n\u003e(...iterables: U): Stream\u003cZipTuple\u003c[Iterable\u003cT\u003e, ...U], undefined\u003e\u003e\n```\n\n* Iteration continues until the longest iterable is exhausted.\n* For uneven lengths, the exhausted iterables will produce `undefined` for the remaining iterations.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [1, 2, 3, 4, 5];\n\nconst stream = Stream.of(input)\n  .zipLongestWith([4, 5, 6]);\n\nfor (const zipped of stream) {\n  // [1, 4], [2, 5], [3, 6], [4, undefined], [5, undefined]\n}\n```\n\n### Terminal operations\n#### Transformation Terminal Operations\n##### Tee\nReturn several independent (duplicated) streams.\n\n```\nStream\u003cT\u003e.tee(count: number): Array\u003cStream\u003cT\u003e\u003e\n```\n\n```php\nimport { Stream } from \"itertools-ts\";\n\nconst daysOfWeek = ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun'];\nconst count = 3;\n\nconst [week1Stream, week2Stream, week3Stream] = Stream.of(daysOfWeek)\n    .tee(count);\n\n// Each weekStream contains ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun']\n```\n\n##### To Array\nReturns an array of stream elements.\n\n```\nStream\u003cT\u003e.toArray(): Array\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst result = Stream.of([1, 2, 3, 4, 5])\n  .map((x) =\u003e x**2)\n  .toArray();\n// [1, 4, 9, 16, 25]\n```\n\n##### To Map\nConverts stream to Map.\n\n```\nStream\u003cT\u003e.toMap(): T extends [infer TKey, infer TValue] ? Map\u003cTKey, TValue\u003e : never\n```\n\nStream collection must contain only key-value pairs as elements.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst result = Stream.of([1, 2, 3])\n  .enumerate()\n  .toMap();\n// Map([[0, 1], [1, 2], [2, 3]])\n```\n\n##### To Set\nConverts stream to Set.\n\n```\nStream\u003cT\u003e.toSet(): Set\u003cT\u003e\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst result = Stream.of([1, 1, 2, 2, 3, 3])\n  .toMap();\n// Set([1, 2, 3])\n```\n\n#### Reduce Terminal Operations\n##### To Average\nReduces iterable source to the mean average of its items.\n\n```\nStream\u003cT\u003e.toAverage(): number | undefined\n```\n\nReturns `undefined` if iterable source is empty.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [2, 4, 6, 8];\n\nconst result = Stream.of(iterable)\n  .toAverage();\n// 5\n```\n\n##### To Count\nReduces iterable source to its length.\n\n```\nStream\u003cT\u003e.toCount(): number\n```\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [10, 20, 30, 40, 50];\n\nconst result = Stream.of(iterable)\n  .toCount();\n// 5\n```\n\n##### To First\nReduces stream to its first element.\n\n```\nStream\u003cT\u003e.toFirst(): T\n```\n\nThrows `LengthException` if stream is empty.\n\n```typescript\nimport { Stream } from \"itertools-ts\";\n\nconst input = [10, 20, 30];\n\nconst result = Stream.of(input)\n  .toFirst();\n// 10\n```\n\n##### To First And Last\nReduces stream to its first last elements.\n\n```\nStream\u003cT\u003e.toFirstAndL","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmoren%2Fitertools-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsmoren%2Fitertools-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsmoren%2Fitertools-ts/lists"}