{"id":13704595,"url":"https://github.com/markrogoyski/itertools-php","last_synced_at":"2025-05-16T12:04:41.916Z","repository":{"id":47480673,"uuid":"381586025","full_name":"markrogoyski/itertools-php","owner":"markrogoyski","description":"PHP Iteration Tools Library","archived":false,"fork":false,"pushed_at":"2025-02-24T03:43:57.000Z","size":992,"stargazers_count":142,"open_issues_count":1,"forks_count":13,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-12T06:18:52.103Z","etag":null,"topics":["fluentinterface","generator","infinite-iteration","iterable","iterator","looping","loops","php","php-library","random-iteration","stream","streams","traversable","zip"],"latest_commit_sha":null,"homepage":"","language":"PHP","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/markrogoyski.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":"2021-06-30T05:31:00.000Z","updated_at":"2025-04-03T20:09:01.000Z","dependencies_parsed_at":"2024-02-27T02:29:59.284Z","dependency_job_id":"0d2d78c1-34ca-4803-ab97-2fb874922b5c","html_url":"https://github.com/markrogoyski/itertools-php","commit_stats":{"total_commits":455,"total_committers":3,"mean_commits":"151.66666666666666","dds":0.4241758241758242,"last_synced_commit":"8f4f313db6e0cf98bc528ba2718d95de85a9a923"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markrogoyski%2Fitertools-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markrogoyski%2Fitertools-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markrogoyski%2Fitertools-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/markrogoyski%2Fitertools-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/markrogoyski","download_url":"https://codeload.github.com/markrogoyski/itertools-php/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248525140,"owners_count":21118620,"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":["fluentinterface","generator","infinite-iteration","iterable","iterator","looping","loops","php","php-library","random-iteration","stream","streams","traversable","zip"],"created_at":"2024-08-02T21:01:12.804Z","updated_at":"2025-04-12T06:19:01.292Z","avatar_url":"https://github.com/markrogoyski.png","language":"PHP","funding_links":[],"categories":["目录","Table of Contents"],"sub_categories":["建筑 Architectural","Architectural","Globalization"],"readme":"![IterToolsLogo Logo](https://github.com/markrogoyski/itertools-php/blob/main/docs/image/IterToolsLogo.png?raw=true)\n\n### IterTools - PHP Iteration Tools to Power Up Your Loops\n\nInspired by Python—designed for PHP.\n\n[![Coverage Status](https://coveralls.io/repos/github/markrogoyski/itertools-php/badge.svg?branch=main)](https://coveralls.io/github/markrogoyski/itertools-php?branch=main)\n[![License](https://poser.pugx.org/markrogoyski/math-php/license)](https://packagist.org/packages/markrogoyski/itertools-php)\n\n### Features\n\nIterTools makes you an iteration superstar by providing two types of tools:\n\n* Loop iteration tools\n* Stream iteration tools\n\n**Loop Iteration Tools Example**\n\n```php\nforeach (Multi::zip(['a', 'b'], [1, 2]) as [$letter, $number]) {\n    print($letter . $number);  // a1, b2\n}\n```\n\n**Stream Iteration Tools Example**\n\n```php\n$result = Stream::of([1, 1, 2, 2, 3, 4, 5])\n    -\u003edistinct()                 // [1, 2, 3, 4, 5]\n    -\u003emap(fn ($x) =\u003e $x**2)      // [1, 4, 9, 16, 25]\n    -\u003efilter(fn ($x) =\u003e $x \u003c 10) // [1, 4, 9]\n    -\u003etoSum();                   // 14\n```\n\nAll functions work on `iterable` collections:\n* `array` (type)\n* `Generator` (type)\n* `Iterator` (interface)\n* `Traversable` (interface)\n\n### README docs translated in other languages:\n* [Русский](docs/README-RU.md)\n\nQuick Reference\n-----------\n\n### Loop Iteration Tools\n\n#### Multi Iteration\n| Iterator                    | Description                                                                             | Code Snippet                                  |\n|-----------------------------|-----------------------------------------------------------------------------------------|-----------------------------------------------|\n| [`chain`](#Chain)           | Chain multiple iterables together                                                       | `Multi::chain($list1, $list2)`                |\n| [`zip`](#Zip)               | Iterate multiple collections simultaneously until the shortest iterator completes       | `Multi::zip($list1, $list2)`                  |\n| [`zipEqual`](#ZipEqual)     | Iterate multiple collections of equal length simultaneously, error if lengths not equal | `Multi::zipEqual($list1, $list2)`             |\n| [`zipFilled`](#ZipFilled)   | Iterate multiple collections, using a filler value if lengths not equal                 | `Multi::zipFilled($default, $list1, $list2)`  |\n| [`zipLongest`](#ZipLongest) | Iterate multiple collections simultaneously until the longest iterator completes        | `Multi::zipLongest($list1, $list2)`           |\n\n#### Single Iteration\n| Iterator                                       | Description                                  | Code Snippet                                                |\n|------------------------------------------------|----------------------------------------------|-------------------------------------------------------------|\n| [`chunkwise`](#Chunkwise)                      | Iterate by chunks                            | `Single::chunkwise($data, $chunkSize)`                      |\n| [`chunkwiseOverlap`](#Chunkwise-Overlap)       | Iterate by overlapped chunks                 | `Single::chunkwiseOverlap($data, $chunkSize, $overlapSize)` |\n| [`compress`](#Compress)                        | Filter out elements not selected             | `Single::compress($data, $selectors)`                       |\n| [`compressAssociative`](#Compress-Associative) | Filter out elements by keys not selected     | `Single::compressAssociative($data, $selectorKeys)`         |\n| [`dropWhile`](#Drop-While)                     | Drop elements while predicate is true        | `Single::dropWhile($data, $predicate)`                      |\n| [`filter`](#Filter)                            | Filter for elements where predicate is true  | `Single::filterTrue($data, $predicate)`                     |\n| [`filterTrue`](#Filter-True)                   | Filter for truthy elements                   | `Single::filterTrue($data)`                                 |\n| [`filterFalse`](#Filter-False)                 | Filter for falsy elements                    | `Single::filterFalse($data)`                                |\n| [`filterKeys`](#Filter-Keys)                   | Filter for keys where predicate is true      | `Single::filterKeys($data, $predicate)`                     |\n| [`flatMap`](#Flat-Map)                         | Map function onto items and flatten result   | `Single::flaMap($data, $mapper)`                            |\n| [`flatten`](#Flatten)                          | Flatten multidimensional iterable            | `Single::flatten($data, [$dimensions])`                     |\n| [`groupBy`](#Group-By)                         | Group data by a common element               | `Single::groupBy($data, $groupKeyFunction, [$itemKeyFunc])` |\n| [`limit`](#Limit)                              | Iterate up to a limit                        | `Single::limit($data, $limit)`                              |\n| [`map`](#Map)                                  | Map function onto each item                  | `Single::map($data, $function)`                             |\n| [`pairwise`](#Pairwise)                        | Iterate successive overlapping pairs         | `Single::pairwise($data)`                                   |\n| [`reindex`](#Reindex)                          | Reindex keys of key-value iterable           | `Single::reindex($data, $reindexer)`                        |\n| [`repeat`](#Repeat)                            | Repeat an item a number of times             | `Single::repeat($item, $repetitions)`                       |\n| [`reverse`](#Reverse)                          | Iterate elements in reverse order            | `Single::reverse($data)`                                    |\n| [`skip`](#Skip)                                | Iterate after skipping elements              | `Single::skip($data, $count, [$offset])`                    |\n| [`slice`](#Slice)                              | Extract a slice of the iterable              | `Single::slice($data, [$start], [$count], [$step])`         |\n| [`string`](#String)                            | Iterate the characters of a string           | `Single::string($string)`                                   |\n| [`takeWhile`](#Take-While)                     | Iterate elements while predicate is true     | `Single::takeWhile($data, $predicate)`                      |\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($collection)`   |\n| [`repeat`](#Repeat-Infinite) | Repeat an item forever     | `Infinite::repeat($item)`        |\n\n#### Random Iteration\n| Iterator                                  | Description                       | Code Snippet                               |\n|-------------------------------------------|-----------------------------------|--------------------------------------------|\n| [`choice`](#Choice)                       | Random selections from list       | `Random::choice($list, $repetitions)`      |\n| [`coinFlip`](#CoinFlip)                   | Random coin flips (0 or 1)        | `Random::coinFlip($repetitions)`           |\n| [`number`](#Number)                       | Random numbers                    | `Random::number($min, $max, $repetitions)` |\n| [`percentage`](#Percentage)               | Random percentage between 0 and 1 | `Random::percentage($repetitions)`         |\n| [`rockPaperScissors`](#RockPaperScissors) | Random rock-paper-scissors hands  | `Random::rockPaperScissors($repetitions)`  |\n\n#### Math Iteration\n| Iterator                                        | Description                             | Code Snippet                                       |\n|-------------------------------------------------|-----------------------------------------|----------------------------------------------------|\n| [`frequencies`](#Frequencies)                   | Frequency distribution of data          | `Math::frequencies($data, [$strict])`              |\n| [`relativeFrequencies`](#Relative-Frequencies)  | Relative frequency distribution of data | `Math::relativeFrequencies($data, [$strict])`      |\n| [`runningAverage`](#Running-Average)            | Running average accumulation            | `Math::runningAverage($numbers, $initialValue)`    |\n| [`runningDifference`](#Running-Difference)      | Running difference accumulation         | `Math::runningDifference($numbers, $initialValue)` |\n| [`runningMax`](#Running-Max)                    | Running maximum accumulation            | `Math::runningMax($numbers, $initialValue)`        |\n| [`runningMin`](#Running-Min)                    | Running minimum accumulation            | `Math::runningMin($numbers, $initialValue)`        |\n| [`runningProduct`](#Running-Product)            | Running product accumulation            | `Math::runningProduct($numbers, $initialValue)`    |\n| [`runningTotal`](#Running-Total)                | Running total accumulation              | `Math::runningTotal($numbers, $initialValue)`      |\n\n#### Set and multiset Iteration\n| Iterator                                                        | Description                                               | Code Snippet                                                 |\n|-----------------------------------------------------------------|-----------------------------------------------------------|--------------------------------------------------------------|\n| [`distinct`](#Distinct)                                         | Iterate only distinct items                               | `Set::distinct($data)`                                       |\n| [`distinctBy`](#Distinct-By)                                    | Iterate only distinct items using custom comparator       | `Set::distinct($data, $compareBy)`                           |\n| [`intersection`](#Intersection)                                 | Intersection of iterables                                 | `Set::intersection(...$iterables)`                           |\n| [`intersectionCoercive`](#Intersection-Coercive)                | Intersection with type coercion                           | `Set::intersectionCoercive(...$iterables)`                   |\n| [`partialIntersection`](#Partial-Intersection)                  | Partial intersection of iterables                         | `Set::partialIntersection($minCount, ...$iterables)`         |\n| [`partialIntersectionCoercive`](#Partial-Intersection-Coercive) | Partial intersection with type coercion                   | `Set::partialIntersectionCoercive($minCount, ...$iterables)` |\n| [`symmetricDifference`](#Symmetric-Difference)                  | Symmetric difference of iterables                         | `Set::symmetricDifference(...$iterables)`                    |\n| [`symmetricDifferenceCoercive`](#Symmetric-Difference-Coercive) | Symmetric difference with type coercion                   | `Set::symmetricDifferenceCoercive(...$iterables)`            |\n| [`union`](#Union)                                               | Union of iterables                                        | `Set::union(...$iterables)`                                  |\n| [`unionCoercive`](#Union-Coercive)                              | Union with type coercion                                  | `Set::unionCoercive(...$iterables)`                          |\n\n#### Sort Iteration\n| Iterator                                       | Description                                  | Code Snippet                                              |\n|------------------------------------------------|----------------------------------------------|-----------------------------------------------------------|\n| [`asort`](#ASort)                              | Iterate a sorted collection maintaining keys | `Sort::asort($data, [$comparator])`                       |\n| [`sort`](#Sort)                                | Iterate a sorted collection                  | `Sort::sort($data, [$comparator])`                        |\n\n#### File Iteration\n| Iterator                                                        | Description                                               | Code Snippet                                                 |\n|-----------------------------------------------------------------|-----------------------------------------------------------|--------------------------------------------------------------|\n| [`readCsv`](#Read-CSV)                                          | Intersection a CSV file line by line                      | `File::readCsv($fileHandle)`                                 |\n| [`readLines`](#Read-Lines)                                      | Iterate a file line by line                               | `File::readLines($fileHandle)`                               |\n\n#### Transform Iteration\n| Iterator                                       | Description                                  | Code Snippet                                                      |\n|------------------------------------------------|----------------------------------------------|-------------------------------------------------------------------|\n| [`tee`](#Tee)                                  | Iterate duplicate iterators                  | `Transform::tee($data, $count)`                                   |\n| [`toArray`](#To-Array)                         | Transform iterable to an array               | `Transform::toArray($data)`                                       |\n| [`toAssociativeArray`](#To-Associative-Array)  | Transform iterable to an associative array   | `Transform::toAssociativeArray($data, [$keyFunc], [$valueFunc])`  |\n| [`toIterator`](#To-Iterator)                   | Transform iterable to an iterator            | `Transform::toIterator($data)`                                    |\n\n#### Summary\n| Summary                                                 | Description                                                              | Code Snippet                                      |\n|---------------------------------------------------------|--------------------------------------------------------------------------|---------------------------------------------------|\n| [`allMatch`](#All-Match)                                | True if all items are true according to predicate                        | `Summary::allMatch($data, $predicate)`            |\n| [`allUnique`](#All-Unique)                              | True if all items are unique                                             | `Summary::allUnique($data, [$strict])`            |\n| [`anyMatch`](#Any-Match)                                | True if any item is true according to predicate                          | `Summary::anyMatch($data, $predicate)`            |\n| [`arePermutations`](#Are-Permutations)                  | True if iterables are permutations of each other                         | `Summary::arePermutations(...$iterables)`         |\n| [`arePermutationsCoercive`](#Are-Permutations-Coercive) | True if iterables are permutations of each other with type coercion      | `Summary::arePermutationsCoercive(...$iterables)` |\n| [`exactlyN`](#Exactly-N)                                | True if exactly n items are true according to predicate                  | `Summary::exactlyN($data, $n, $predicate)`        |\n| [`isEmpty`](#Is-Empty)                                  | True if iterable has no items                                            | `Summary::isEmpty($data)`                         |\n| [`isPartitioned`](#Is-Partitioned)                      | True if partitioned with items true according to predicate before others | `Summary::isPartitioned($data, $predicate)`       |\n| [`isSorted`](#Is-Sorted)                                | True if iterable sorted                                                  | `Summary::isSorted($data)`                        |\n| [`isReversed`](#Is-Reversed)                            | True if iterable reverse sorted                                          | `Summary::isReversed($data)`                      |\n| [`noneMatch`](#None-Match)                              | True if none of items true according to predicate                        | `Summary::noneMatch($data, $predicate)`           |\n| [`same`](#Same)                                         | True if iterables are the same                                           | `Summary::same(...$iterables)`                    |\n| [`sameCount`](#Same-Count)                              | True if iterables have the same lengths                                  | `Summary::sameCount(...$iterables)`               |\n\n#### Reduce\n| Reducer                                | Description                                | Code Snippet                                                  |\n|----------------------------------------|--------------------------------------------|---------------------------------------------------------------|\n| [`toAverage`](#To-Average)             | Mean average of elements                   | `Reduce::toAverage($numbers)`                                 |\n| [`toCount`](#To-Count)                 | Reduce to length of iterable               | `Reduce::toCount($data)`                                      |\n| [`toFirst`](#To-First)                 | Reduce to its first value                  | `Reduce::toFirst($data)`                                      |\n| [`toFirstAndLast`](#To-First-And-Last) | Reduce to its first and last values        | `Reduce::toFirstAndLast($data)`                               |\n| [`toLast`](#To-Last)                   | Reduce to its last value                   | `Reduce::toLast()`                                            |\n| [`toMax`](#To-Max)                     | Reduce to its largest element              | `Reduce::toMax($numbers, [$compareBy])`                       |\n| [`toMin`](#To-Min)                     | Reduce to its smallest element             | `Reduce::toMin($numbers, [$compareBy])`                       |\n| [`toMinMax`](#To-Min-Max)              | Reduce to array of upper and lower bounds  | `Reduce::toMinMax($numbers, [$compareBy])`                    |\n| [`toNth`](#To-Nth)                     | Reduce to value at nth position            | `Reduce::toNth($data, $position)`                             |\n| [`toProduct`](#To-Product)             | Reduce to the product of its elements      | `Reduce::toProduct($numbers)`                                 |\n| [`toRandomValue`](#To-Random-Value)    | Reduce to random value from iterable       | `Reduce::toRandomValue($data)`                                |\n| [`toRange`](#To-Range)                 | Reduce to difference of max and min values | `Reduce::toRange($numbers)`                                   |\n| [`toString`](#To-String)               | Reduce to joined string                    | `Reduce::toString($data, [$separator], [$prefix], [$suffix])` |\n| [`toSum`](#To-Sum)                     | Reduce to the sum of its elements          | `Reduce::toSum($numbers)`                                     |\n| [`toValue`](#To-Value)                 | Reduce to value using callable reducer     | `Reduce::toValue($data, $reducer, $initialValue)`             |\n\n### Stream Iteration Tools\n#### Stream Sources\n| Source                                           | Description                                                     | Code Snippet                                        |\n|--------------------------------------------------|-----------------------------------------------------------------|-----------------------------------------------------|\n| [`of`](#Of)                                      | Create a stream from an iterable                                | `Stream::of($iterable)`                             |\n| [`ofCoinFlips`](#Of-Coin-Flips)                  | Create a stream of random coin flips                            | `Stream::ofCoinFlips($repetitions)`                 |\n| [`ofCsvFile`](#Of-CSV-File)                      | Create a stream from a CSV file                                 | `Stream::ofCsvFile($fileHandle)`                    |\n| [`ofEmpty`](#Of-Empty)                           | Create an empty stream                                          | `Stream::ofEmpty()`                                 |\n| [`ofFileLines`](#Of-File-Lines)                  | Create a stream from lines of a file                            | `Stream::ofFileLines($fileHandle)`                  |\n| [`ofRandomChoice`](#Of-Random-Choice)            | Create a stream of random selections                            | `Stream::ofRandomChoice($items, $repetitions)`      |\n| [`ofRandomNumbers`](#Of-Random-Numbers)          | Create a stream of random numbers (integers)                    | `Stream::ofRandomNumbers($min, $max, $repetitions)` |\n| [`ofRandomPercentage`](#Of-Random-Percentage)    | Create a stream of random percentages between 0 and 1           | `Stream::ofRandomPercentage($repetitions)`          |\n| [`ofRange`](#Of-Range)                           | Create a stream of a range of numbers                           | `Stream::ofRange($start, $end, $step)`              |\n| [`ofRockPaperScissors`](#Of-Rock-Paper-Scissors) | Create a stream of rock-paper-scissors hands                    | `Stream::ofRockPaperScissors($repetitions)`         |\n\n#### Stream Operations\n| Operation                                                                 | Description                                                                               | Code Snippet                                                                      |\n|---------------------------------------------------------------------------|-------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|\n| [`asort`](#ASort-1)                                                       | Sorts the iterable source maintaining keys                                                | `$stream-\u003easort([$comparator])`                                                   |\n| [`chainWith`](#Chain-With)                                                | Chain iterable source withs given iterables together into a single iteration              | `$stream-\u003echainWith(...$iterables)`                                               |\n| [`compress`](#Compress-1)                                                 | Compress source by filtering out data not selected                                        | `$stream-\u003ecompress($selectors)`                                                   |\n| [`compressAssociative`](#Compress-Associative-1)                          | Compress source by filtering out keys not selected                                        | `$stream-\u003ecompressAssociative($selectorKeys)`                                     |\n| [`chunkwise`](#Chunkwise-1)                                               | Iterate by chunks                                                                         | `$stream-\u003echunkwise($chunkSize)`                                                  |\n| [`chunkwiseOverlap`](#Chunkwise-Overlap-1)                                | Iterate by overlapped chunks                                                              | `$stream-\u003echunkwiseOverlap($chunkSize, $overlap)`                                 |\n| [`distinct`](#Distinct-1)                                                 | Filter out elements: iterate only unique items                                            | `$stream-\u003edistinct([$strict])`                                                    |\n| [`distinctBy`](#Distinct-By-1)                                            | Filter out elements: iterate only unique items using custom comparator                    | `$stream-\u003edistinct($compareBy)`                                                   |\n| [`dropWhile`](#Drop-While-1)                                              | Drop elements from the iterable source while the predicate function is true               | `$stream-\u003edropWhile($predicate)`                                                  |\n| [`filter`](#Filter-1)                                                     | Filter for only elements where the predicate function is true                             | `$stream-\u003efilterTrue($predicate)`                                                 |\n| [`filterTrue`](#Filter-True-1)                                            | Filter for only truthy elements                                                           | `$stream-\u003efilterTrue()`                                                           |\n| [`filterFalse`](#Filter-False-1)                                          | Filter for only falsy elements                                                            | `$stream-\u003efilterFalse()`                                                          |\n| [`filterKeys`](#Filter-Keys-1)                                            | Filter for keys where predicate function is true                                          | `$stream-\u003efilterKeys($predicate)`                                                 |\n| [`flatMap`](#Flat-Map-1)                                                  | Map function onto elements and flatten result                                             | `$stream-\u003eflatMap($function)`                                                     |\n| [`flatten`](#Flatten-1)                                                   | Flatten multidimensional stream                                                           | `$stream-\u003eflatten($dimensions)`                                                   |\n| [`frequencies`](#Frequencies-1)                                           | Frequency distribution                                                                    | `$stream-\u003efrequencies([$strict])`                                                 |\n| [`groupBy`](#Group-By-1)                                                  | Group iterable source by a common data element                                            | `$stream-\u003egroupBy($groupKeyFunction, [$itemKeyFunc])`                             |\n| [`infiniteCycle`](#Infinite-Cycle)                                        | Cycle through the elements of iterable source sequentially forever                        | `$stream-\u003einfiniteCycle()`                                                        |\n| [`intersectionWith`](#Intersection-With)                                  | Intersect iterable source and given iterables                                             | `$stream-\u003eintersectionWith(...$iterables)`                                        |\n| [`intersection CoerciveWith`](#Intersection-Coercive-With)                | Intersect iterable source and given iterables with type coercion                          | `$stream-\u003eintersectionCoerciveWith(...$iterables)`                                |\n| [`limit`](#Limit-1)                                                       | Limit the stream's iteration                                                              | `$stream-\u003elimit($limit)`                                                          |\n| [`map`](#Map-1)                                                           | Map function onto elements                                                                | `$stream-\u003emap($function)`                                                         |\n| [`pairwise`](#Pairwise-1)                                                 | Return pairs of elements from iterable source                                             | `$stream-\u003epairwise()`                                                             |\n| [`partialIntersectionWith`](#Partial-Intersection-With)                   | Partially intersect iterable source and given iterables                                   | `$stream-\u003epartialIntersectionWith( $minIntersectionCount, ...$iterables)`         |\n| [`partialIntersection CoerciveWith`](#Partial-Intersection-Coercive-With) | Partially intersect iterable source and given iterables with type coercion                | `$stream-\u003epartialIntersectionCoerciveWith( $minIntersectionCount, ...$iterables)` |\n| [`reindex`](#Reindex-1)                                                   | Reindex keys of key-value stream                                                          | `$stream-\u003ereindex($reindexer)`                                                    |\n| [`relativeFrequencies`](#Relative-Frequencies-1)                          | Relative frequency distribution                                                           | `$stream-\u003erelativeFrequencies([$strict])`                                         |\n| [`reverse`](#Reverse-1)                                                   | Reverse elements of the stream                                                            | `$stream-\u003ereverse()`                                                              |\n| [`runningAverage`](#Running-Average-1)                                    | Accumulate the running average (mean) over iterable source                                | `$stream-\u003erunningAverage($initialValue)`                                          |\n| [`runningDifference`](#Running-Difference-1)                              | Accumulate the running difference over iterable source                                    | `$stream-\u003erunningDifference($initialValue)`                                       |\n| [`runningMax`](#Running-Max-1)                                            | Accumulate the running max over iterable source                                           | `$stream-\u003erunningMax($initialValue)`                                              |\n| [`runningMin`](#Running-Min-1)                                            | Accumulate the running min over iterable source                                           | `$stream-\u003erunningMin($initialValue)`                                              |\n| [`runningProduct`](#Running-Product-1)                                    | Accumulate the running product over iterable source                                       | `$stream-\u003erunningProduct($initialValue)`                                          |\n| [`runningTotal`](#Running-Total-1)                                        | Accumulate the running total over iterable source                                         | `$stream-\u003erunningTotal($initialValue)`                                            |\n| [`skip`](#Skip-1)                                                         | Skip some elements of the stream                                                          | `$stream-\u003eskip($count, [$offset])`                                                |\n| [`slice`](#Slice-1)                                                       | Extract a slice of the stream                                                             | `$stream-\u003eslice([$start], [$count], [$step])`                                     |\n| [`sort`](#Sort-1)                                                         | Sorts the stream                                                                          | `$stream-\u003esort([$comparator])`                                                    |\n| [`symmetricDifferenceWith`](#Symmetric-Difference-With)                   | Symmetric difference of iterable source and given iterables                               | `$this-\u003esymmetricDifferenceWith(...$iterables)`                                   |\n| [`symmetricDifference CoerciveWith`](#Symmetric-Difference-Coercive-With) | Symmetric difference of iterable source and given iterables with type coercion            | `$this-\u003esymmetricDifferenceCoerciveWith( ...$iterables)`                          |\n| [`takeWhile`](#Take-While-1)                                              | Return elements from the iterable source as long as the predicate is true                 | `$stream-\u003etakeWhile($predicate)`                                                  |\n| [`unionWith`](#Union-With)                                                | Union of stream with iterables                                                            | `$stream-\u003eunionWith(...$iterables)`                                               |\n| [`unionCoerciveWith`](#Union-Coercive-With)                               | Union of stream with iterables with type coercion                                         | `$stream-\u003eunionCoerciveWith(...$iterables)`                                       |\n| [`zipWith`](#Zip-With)                                                    | Iterate iterable source with another iterable collection simultaneously                   | `$stream-\u003ezipWith(...$iterables)`                                                 |\n| [`zipEqualWith`](#Zip-Equal-With)                                         | Iterate iterable source with another iterable collection of equal lengths simultaneously  | `$stream-\u003ezipEqualWith(...$iterables)`                                            |\n| [`zipFilledWith`](#Zip-Filled-With)                                       | Iterate iterable source with another iterable collection using default filler             | `$stream-\u003ezipFilledWith($default, ...$iterables)`                                 |\n| [`zipLongestWith`](#Zip-Longest-With)                                     | Iterate iterable source with another iterable collection simultaneously                   | `$stream-\u003ezipLongestWith(...$iterables)`                                          |\n\n#### Stream Terminal Operations\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-\u003eallMatch($predicate)`                        |\n| [`allUnique`](#All-Unique-1)                                     | Returns true if all items in stream are unique                                   | `$stream-\u003eallUnique([$strict]])`                       |\n| [`anyMatch`](#Any-Match-1)                                       | Returns true if any item in stream matches predicate                             | `$stream-\u003eanyMatch($predicate)`                        |\n| [`arePermutationsWith`](#Are-Permutations-With)                  | Returns true if all iterables permutations of stream                             | `$stream-\u003earePermutationsWith(...$iterables)`          |\n| [`arePermutationsCoerciveWith`](#Are-Permutations-Coercive-With) | Returns true if all iterables permutations of stream with type coercion          | `$stream-\u003earePermutationsCoerciveWith(...$iterables)`  |\n| [`exactlyN`](#Exactly-N-1)                                       | Returns true if exactly n items are true according to predicate                  | `$stream-\u003eexactlyN($n, $predicate)`                    |\n| [`isEmpty`](#Is-Empty-1)                                         | Returns true if stream has no items                                              | `$stream::isEmpty()`                                   |\n| [`isPartitioned`](#Is-Partitioned-1)                             | Returns true if partitioned with items true according to predicate before others | `$stream::isPartitioned($predicate)`                   |\n| [`isSorted`](#Is-Sorted-1)                                       | Returns true if stream is sorted in ascending order                              | `$stream-\u003eisSorted()`                                  |\n| [`isReversed`](#Is-Reversed-1)                                   | Returns true if stream is sorted in reverse descending order                     | `$stream-\u003eisReversed()`                                |\n| [`noneMatch`](#None-Match-1)                                     | Returns true if none of the items in stream match predicate                      | `$stream-\u003enoneMatch($predicate)`                       |\n| [`sameWith`](#Same-With)                                         | Returns true if stream and all given collections are the same                    | `$stream-\u003esameWith(...$iterables)`                     |\n| [`sameCountWith`](#Same-Count-With)                              | Returns true if stream and all given collections have the same lengths           | `$stream-\u003esameCountWith(...$iterables)`                |\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-\u003etoAverage()`                                  |\n| [`toCount`](#To-Count-1)                 | Reduces stream to its length                       | `$stream-\u003etoCount()`                                    |\n| [`toFirst`](#To-First-1)                 | Reduces stream to its first value                  | `$stream-\u003etoFirst()`                                    |\n| [`toFirstAndLast`](#To-First-And-Last-1) | Reduces stream to its first and last values        | `$stream-\u003etoFirstAndLast()`                             |\n| [`toLast`](#To-Last-1)                   | Reduces stream to its last value                   | `$stream-\u003etoLast()`                                     |\n| [`toMax`](#To-Max-1)                     | Reduces stream to its max value                    | `$stream-\u003etoMax([$compareBy])`                          |\n| [`toMin`](#To-Min-1)                     | Reduces stream to its min value                    | `$stream-\u003etoMin([$compareBy])`                          |\n| [`toMinMax`](#To-Min-Max-1)              | Reduces stream to array of upper and lower bounds  | `$stream-\u003etoMinMax([$compareBy])`                       |\n| [`toNth`](#To-Nth-1)                     | Reduces stream to value at nth position            | `$stream-\u003etoNth($position)`                             |\n| [`toProduct`](#To-Product-1)             | Reduces stream to the product of its items         | `$stream-\u003etoProduct()`                                  |\n| [`toString`](#To-String-1)               | Reduces stream to joined string                    | `$stream-\u003etoString([$separator], [$prefix], [$suffix])` |\n| [`toSum`](#To-Sum-1)                     | Reduces stream to the sum of its items             | `$stream-\u003etoSum()`                                      |\n| [`toRandomValue`](#To-Random-Value-1)    | Reduces stream to random value within it           | `$stream-\u003etoRandomValue()`                              |\n| [`toRange`](#To-Range-1)                 | Reduces stream to difference of max and min values | `$stream-\u003etoRange()`                                    |\n| [`toValue`](#To-Value-1)                 | Reduces stream like array_reduce() function        | `$stream-\u003etoValue($reducer, $initialValue)`             |\n\n##### Transformation Terminal Operations\n| Terminal Operation                              | Description                                           | Code Snippet                                            |\n|-------------------------------------------------|-------------------------------------------------------|---------------------------------------------------------|\n| [`toArray`](#To-Array-1)                        | Returns array of stream elements                      | `$stream-\u003etoArray()`                                    |\n| [`toAssociativeArray`](#To-Associative-Array-1) | Returns key-value map of stream elements              | `$stream-\u003etoAssociativeArray($keyFunc, $valueFunc)`     |\n| [`tee`](#Tee-1)                                 | Returns array of multiple identical Streams           | `$stream-\u003etee($count)`                                  |\n\n##### Side Effect Terminal Operations\n| Terminal Operation              | Description                                    | Code Snippet                                         |\n|---------------------------------|------------------------------------------------|------------------------------------------------------|\n| [`callForEach`](#Call-For-Each) | Perform action via function on each item       | `$stream-\u003ecallForEach($function)`                    |\n| [`print`](#Print)               | `print` each item in the stream                | `$stream-\u003eprint([$separator], [$prefix], [$suffix])` |\n| [`printLn`](#Print-Line)        | `print` each item on a new line                | `$stream-\u003eprintLn()`                                 |\n| [`toCsvFile`](#To-CSV-File)     | Write the contents of the stream to a CSV file | `$stream-\u003etoCsvFile($fileHandle, [$headers])`        |\n| [`toFile`](#To-File)            | Write the contents of the stream to a file     | `$stream-\u003etoFile($fileHandle)`                       |\n\n#### Stream Debug Operations\n| Debug Operation              | Description                                              | Code Snippet                     |\n|------------------------------|----------------------------------------------------------|----------------------------------|\n| [`peek`](#Peek)              | Peek at each element between stream operations           | `$stream-\u003epeek($peekFunc)`       |\n| [`peekStream`](#Peek-Stream) | Peek at the entire stream between operations             | `$stream-\u003epeekStream($peekFunc)` |\n| [`peekPrint`](#Peek-Print)   | Peek at each element by printing between operations      | `$stream-\u003epeekPrint()`           |\n| [`peekPrintR`](#Peek-PrintR) | Peek at each element by doing print-r between operations | `$stream-\u003epeekPrintR()`          |\n| [`printR`](#Print-R)         | `print_r` each item                                      | `$stream-\u003eprintR()`              |\n| [`varDump`](#Var-Dump)       | `var_dump` each item                                     | `$stream-\u003evarDump()`             |\n\nSetup\n-----\n\n Add the library to your `composer.json` file in your project:\n\n```json\n{\n  \"require\": {\n      \"markrogoyski/itertools-php\": \"1.*\"\n  }\n}\n```\n\nUse [composer](http://getcomposer.org) to install the library:\n\n```bash\n$ php composer.phar install\n```\n\nComposer will install IterTools inside your vendor folder. Then you can add the following to your\n.php files to use the library with Autoloading.\n\n```php\nrequire_once __DIR__ . '/vendor/autoload.php';\n```\n\nAlternatively, use composer on the command line to require and install IterTools:\n\n```\n$ php composer.phar require markrogoyski/itertools-php:1.*\n```\n\n#### Minimum Requirements\n * PHP 7.4\n\nUsage\n-----\nAll functions work on `iterable` collections:\n* `array` (type)\n* `Generator` (type)\n* `Iterator` (interface)\n* `Traversable` (interface)\n\n## Multi Iteration\n### Chain\nChain multiple iterables together into a single continuous sequence.\n\n```Multi::chain(iterable ...$iterables)```\n```php\nuse IterTools\\Multi;\n\n$prequels  = ['Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith'];\n$originals = ['A New Hope', 'Empire Strikes Back', 'Return of the Jedi'];\n\nforeach (Multi::chain($prequels, $originals) as $movie) {\n    print($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```Multi::zip(iterable ...$iterables)```\n```php\nuse IterTools\\Multi;\n\n$languages = ['PHP', 'Python', 'Java', 'Go'];\n$mascots   = ['elephant', 'snake', 'bean', 'gopher'];\n\nforeach (Multi::zip($languages, $mascots) as [$language, $mascot]) {\n    print(\"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```php\n$names          = ['Ryu', 'Ken', 'Chun Li', 'Guile'];\n$countries      = ['Japan', 'USA', 'China', 'USA'];\n$signatureMoves = ['hadouken', 'shoryuken', 'spinning bird kick', 'sonic boom'];\n\nforeach (Multi::zip($names, $countries, $signatureMoves) as [$name, $country, $signatureMove]) {\n    $streetFighter = new StreetFighter($name, $country, $signatureMove);\n}\n```\nNote: For uneven lengths, iteration stops when the shortest iterable is exhausted.\n\n### ZipEqual\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```Multi::zipEqual(iterable ...$iterables)```\n\n```php\nuse IterTools\\Multi;\n\n$letters = ['A', 'B', 'C'];\n$numbers = [1, 2, 3];\n\nforeach (Multi::zipEqual($letters, $numbers) as [$letter, $number]) {\n    // ['A', 1], ['B', 2], ['C', 3]\n}\n```\n\n### ZipFilled\nIterate multiple iterable collections simultaneously, using a default filler value if lengths are not equal.\n\n```Multi::zipFilled(mixed $filler, iterable ...$iterables)```\n\n```php\nuse IterTools\\Multi;\n\n$default = '?';\n$letters = ['A', 'B'];\n$numbers = [1, 2, 3];\n\nforeach (Multi::zipFilled($default, $letters, $numbers) as [$letter, $number]) {\n    // ['A', 1], ['B', 2], ['?', 3]\n}\n```\n\n### ZipLongest\nIterate multiple iterable collections simultaneously.\n\n```Multi::zipLongest(iterable ...$iterables)```\n\nFor uneven lengths, the exhausted iterables will produce `null` for the remaining iterations.\n\n```php\nuse IterTools\\Multi;\n\n$letters = ['A', 'B', 'C'];\n$numbers = [1, 2];\n\nforeach (Multi::zipLongest($letters, $numbers) as [$letter, $number]) {\n    // ['A', 1], ['B', 2], ['C', null]\n}\n```\n\n## Single Iteration\n### Chunkwise\nReturn elements in chunks of a certain size.\n\n```Single::chunkwise(iterable $data, int $chunkSize)```\n\nChunk size must be at least 1.\n\n```php\nuse IterTools\\Single;\n\n$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];\n\nforeach (Single::chunkwise($movies, 3) as $trilogy) {\n    $trilogies[] = $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```Single::chunkwiseOverlap(iterable $data, int $chunkSize, int $overlapSize, bool $includeIncompleteTail = true)```\n\n* Chunk size must be at least 1.\n* Overlap size must be less than chunk size.\n\n```php\nuse IterTools\\Single;\n\n$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n\nforeach (Single::chunkwiseOverlap($numbers, 3, 1) as $chunk) {\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```Single::compress(string $data, $selectors)```\n\n```php\nuse IterTools\\Single;\n\n$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];\n$goodMovies = [0, 0, 0, 1, 1, 1, 1, 0, 0];\n\nforeach (Single::compress($movies, $goodMovies) as $goodMovie) {\n    print($goodMovie);\n}\n// 'A New Hope', 'Empire Strikes Back', 'Return of the Jedi', 'The Force Awakens'\n```\n\n### Compress Associative\nCompress an iterable by filtering out keys that are not selected.\n\n```Single::compressAssociative(string $data, array $selectorKeys)```\n\n* Standard PHP array/iterator keys only (string, integer).\n\n```php\nuse IterTools\\Single;\n\n$starWarsEpisodes = [\n    'I'    =\u003e 'The Phantom Menace',\n    'II'   =\u003e 'Attack of the Clones',\n    'III'  =\u003e 'Revenge of the Sith',\n    'IV'   =\u003e 'A New Hope',\n    'V'    =\u003e 'The Empire Strikes Back',\n    'VI'   =\u003e 'Return of the Jedi',\n    'VII'  =\u003e 'The Force Awakens',\n    'VIII' =\u003e 'The Last Jedi',\n    'IX'   =\u003e 'The Rise of Skywalker',\n];\n$originalTrilogyNumbers = ['IV', 'V', 'VI'];\n\nforeach (Single::compressAssociative($starWarsEpisodes, $originalTrilogyNumbers) as $episode =\u003e $title) {\n    print(\"$episode: $title\" . \\PHP_EOL);\n}\n// IV: A New Hope\n// V: The Empire Strikes Back\n// VI: Return of the Jedi\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```Single::dropWhile(iterable $data, callable $predicate)```\n\n```php\nuse IterTools\\Single;\n\n$scores    = [50, 60, 70, 85, 65, 90];\n$predicate = fn ($x) =\u003e $x \u003c 70;\n\nforeach (Single::dropWhile($scores, $predicate) as $score) {\n    print($score);\n}\n// 70, 85, 65, 90\n```\n\n### Filter\nFilter out elements from the iterable only returning elements where the predicate function is true.\n\n```Single::filter(iterable $data, callable $predicate)```\n\n```php\nuse IterTools\\Single;\n\n$starWarsEpisodes   = [1, 2, 3, 4, 5, 6, 7, 8, 9];\n$goodMoviePredicate = fn ($episode) =\u003e $episode \u003e 3 \u0026\u0026 $episode \u003c 8;\n\nforeach (Single::filter($starWarsEpisodes, $goodMoviePredicate) as $goodMovie) {\n    print($goodMovie);\n}\n// 4, 5, 6, 7\n```\n\n### Filter True\nFilter out elements from the iterable only returning elements that are truthy.\n\n```Single::filterTrue(iterable $data)```\n\n```php\nuse IterTools\\Single;\n\n$reportCardGrades = [100, 0, 95, 85, 0, 94, 0];\n\nforeach (Single::filterTrue($reportCardGrades) as $goodGrade) {\n    print($goodGrade);\n}\n// 100, 95, 85, 94\n```\n\n### Filter False\nFilter out elements from the iterable only returning elements where the predicate function is false.\n\nIf no predicate is provided, the boolean value of the data is used.\n\n```Single::filterFalse(iterable $data, callable $predicate)```\n\n```php\nuse IterTools\\Single;\n\n$alerts = [0, 1, 1, 0, 1, 0, 0, 1, 1];\n\nforeach (Single::filterFalse($alerts) as $noAlert) {\n    print($noAlert);\n}\n// 0, 0, 0, 0\n```\n\n### Filter Keys\nFilter out elements from the iterable only returning elements for which keys the predicate function is true.\n\n```Single::filterKeys(iterable $data, callable $predicate)```\n\n```php\nuse IterTools\\Single;\n\n$olympics = [\n    2000 =\u003e 'Sydney',\n    2002 =\u003e 'Salt Lake City',\n    2004 =\u003e 'Athens',\n    2006 =\u003e 'Turin',\n    2008 =\u003e 'Beijing',\n    2010 =\u003e 'Vancouver',\n    2012 =\u003e 'London',\n    2014 =\u003e 'Sochi',\n    2016 =\u003e 'Rio de Janeiro',\n    2018 =\u003e 'Pyeongchang',\n    2020 =\u003e 'Tokyo',\n    2022 =\u003e 'Beijing',\n];\n\n$summerFilter = fn ($year) =\u003e $year % 4 === 0;\n\nforeach (Single::filterKeys($olympics, $summerFilter) as $year =\u003e $hostCity) {\n    print(\"$year: $hostCity\" . \\PHP_EOL);\n}\n// 2000: Sydney\n// 2004: Athens\n// 2008: Beijing\n// 2012: London\n// 2016: Rio de Janeiro\n// 2020: Tokyo\n```\n\n### Flat Map\nMap a function only the elements of the iterable and then flatten the results.\n\n```Single::flatMap(iterable $data, callable $mapper)```\n\n```php\nuse IterTools\\Single;\n\n$data   = [1, 2, 3, 4, 5];\n$mapper = fn ($item) =\u003e [$item, -$item];\n\nforeach (Single::flatMap($data, $mapper) as $number) {\n    print($number . ' ');\n}\n// 1 -1 2 -2 3 -3 4 -4 5 -5\n```\n\n### Flatten\nFlatten a multidimensional iterable.\n\n```Single::flatten(iterable $data, int $dimensions = 1)```\n\n```php\nuse IterTools\\Single;\n\n$multidimensional = [1, [2, 3], [4, 5]];\n\n$flattened = [];\nforeach (Single::flatten($multidimensional) as $number) {\n    $flattened[] = $number;\n}\n// [1, 2, 3, 4, 5]\n```\n\n### Group By\nGroup data by a common data element.\n\n```Single::groupBy(iterable $data, callable $groupKeyFunction, callable $itemKeyFunction = null)```\n\n* The `$groupKeyFunction` determines the key to group elements by.\n* The optional `$itemKeyFunction` allows custom indexes within each group member.\n\n```php\nuse IterTools\\Single;\n\n$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\n$charactersGroupedByAnimal = [];\nforeach (Single::groupBy($cartoonCharacters, fn ($x) =\u003e $x[1]) as $animal =\u003e $characters) {\n    $charactersGroupedByAnimal[$animal] = $characters;\n}\n/*\n'cat' =\u003e [\n    ['Garfield', 'cat'],\n    ['Tom', 'cat'],\n    ['Felix', 'cat'],\n    ['Heathcliff', 'cat'],\n],\n'dog' =\u003e [\n    ['Snoopy', 'dog'],\n    ['Scooby-Doo', 'dog'],\n    ['Odie', 'dog'],\n],\n'duck' =\u003e [\n    ['Donald', 'duck'],\n    ['Daffy', 'duck'],\n*/\n```\n\n### Limit\nIterate up to a limit.\n\nStops even if more data available if limit reached.\n\n```Single::limit(iterable $data, int $limit)```\n\n```php\nuse IterTools\\Single;\n\n$matrixMovies = ['The Matrix', 'The Matrix Reloaded', 'The Matrix Revolutions', 'The Matrix Resurrections'];\n$limit        = 1;\n\nforeach (Single::limit($matrixMovies, $limit) as $goodMovie) {\n    print($goodMovie);\n}\n// 'The Matrix' (and nothing else)\n```\n\n### Map\nMap a function onto each element.\n\n```Single::map(iterable $data, callable $function)```\n\n```php\nuse IterTools\\Single;\n\n$grades               = [100, 99, 95, 98, 100];\n$strictParentsOpinion = fn ($g) =\u003e $g === 100 ? 'A' : 'F';\n\nforeach (Single::map($grades, $strictParentsOpinion) as $actualGrade) {\n    print($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```Single::pairwise(iterable $data)```\n\n```php\nuse IterTools\\Single;\n\n$friends = ['Ross', 'Rachel', 'Chandler', 'Monica', 'Joey', 'Phoebe'];\n\nforeach (Single::pairwise($friends) as [$leftFriend, $rightFriend]) {\n    print(\"{$leftFriend} and {$rightFriend}\");\n}\n// Ross and Rachel, Rachel and Chandler, Chandler and Monica, ...\n```\n\n### Repeat\nRepeat an item.\n\n```Single::repeat(mixed $item, int $repetitions)```\n\n```php\nuse IterTools\\Single;\n\n$data        = 'Beetlejuice';\n$repetitions = 3;\n\nforeach (Single::repeat($data, $repetitions) as $repeated) {\n    print($repeated);\n}\n// 'Beetlejuice', 'Beetlejuice', 'Beetlejuice'\n```\n\n### Reindex\nReindex keys of key-value iterable using indexer function.\n\n```Single::reindex(string $data, callable $indexer)```\n\n```php\nuse IterTools\\Single;\n\n$data = [\n    [\n        'title'   =\u003e 'Star Wars: Episode IV – A New Hope',\n        'episode' =\u003e 'IV',\n        'year'    =\u003e 1977,\n    ],\n    [\n        'title'   =\u003e 'Star Wars: Episode V – The Empire Strikes Back',\n        'episode' =\u003e 'V',\n        'year'    =\u003e 1980,\n    ],\n    [\n        'title' =\u003e 'Star Wars: Episode VI – Return of the Jedi',\n        'episode' =\u003e 'VI',\n        'year' =\u003e 1983,\n    ],\n];\n$reindexFunc = fn (array $swFilm) =\u003e $swFilm['episode'];\n\n$reindexedData = [];\nforeach (Single::reindex($data, $reindexFunc) as $key =\u003e $filmData) {\n    $reindexedData[$key] = $filmData;\n}\n// [\n//     'IV' =\u003e [\n//         'title'   =\u003e 'Star Wars: Episode IV – A New Hope',\n//         'episode' =\u003e 'IV',\n//         'year'    =\u003e 1977,\n//     ],\n//     'V' =\u003e [\n//         'title'   =\u003e 'Star Wars: Episode V – The Empire Strikes Back',\n//         'episode' =\u003e 'V',\n//         'year'    =\u003e 1980,\n//     ],\n//     'VI' =\u003e [\n//         'title' =\u003e 'Star Wars: Episode VI – Return of the Jedi',\n//         'episode' =\u003e 'VI',\n//         'year' =\u003e 1983,\n//     ],\n// ]\n```\n\n### Reverse\nReverse the elements of an iterable.\n\n```Single::reverse(iterable $data)```\n\n```php\nuse IterTools\\Single;\n\n$words = ['Alice', 'answers', 'your', 'questions', 'Bob'];\n\nforeach (Single::reverse($words) as $word) {\n    print($word . ' ');\n}\n// Bob questions your answers Alice\n```\n\n### Skip\nSkip n elements in the iterable after optional offset offset.\n\n```Single::skip(iterable $data, int $count, int $offset = 0)```\n\n```php\nuse IterTools\\Single;\n\n$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\n$prequelsRemoved = [];\nforeach (Single::skip($movies, 3) as $nonPrequel) {\n    $prequelsRemoved[] = $nonPrequel;\n} // Episodes IV - IX\n\n$onlyTheBest = [];\nforeach (Single::skip($prequelsRemoved, 3, 3) as $nonSequel) {\n    $onlyTheBest[] = $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```Single::slice(iterable $data, int $start = 0, int $count = null, int $step = 1)```\n\n```php\nuse IterTools\\Single;\n\n$olympics = [1992, 1994, 1996, 1998, 2000, 2002, 2004, 2006, 2008, 2010, 2012, 2014, 2016, 2018, 2020, 2022];\n$winterOlympics = [];\n\nforeach (Single::slice($olympics, 1, 8, 2) as $winterYear) {\n    $winterOlympics[] = $winterYear;\n}\n// [1994, 1998, 2002, 2006, 2010, 2014, 2018, 2022]\n```\n\n### String\nIterate the individual characters of a string.\n\n```Single::string(string $string)```\n\n```php\nuse IterTools\\Single;\n\n$string = 'MickeyMouse';\n\n$listOfCharacters = [];\nforeach (Single::string($string) as $character) {\n    $listOfCharacters[] = $character;\n}\n// ['M', 'i', 'c', 'k', 'e', 'y', 'M', 'o', 'u', 's', 'e']\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```Single::takeWhile(iterable $data, callable $predicate)```\n\n```php\nuse IterTools\\Single;\n\n$prices = [0, 0, 5, 10, 0, 0, 9];\n$isFree = fn ($price) =\u003e $price == 0;\n\nforeach (Single::takeWhile($prices, $isFree) as $freePrice) {\n    print($freePrice);\n}\n// 0, 0\n```\n\n## Infinite Iteration\n### Count\nCount sequentially forever.\n\n```Infinite::count(int $start = 1, int $step = 1)```\n\n```php\nuse IterTools\\Infinite;\n\n$start = 1;\n$step  = 1;\n\nforeach (Infinite::count($start, $step) as $i) {\n    print($i);\n}\n// 1, 2, 3, 4, 5 ...\n```\n\n### Cycle\nCycle through the elements of a collection sequentially forever.\n\n```Infinite::cycle(iterable $iterable)```\n\n```php\nuse IterTools\\Infinite;\n\n$hands = ['rock', 'paper', 'scissors'];\n\nforeach (Infinite::cycle($hands) as $hand) {\n    RockPaperScissors::playHand($hand);\n}\n// rock, paper, scissors, rock, paper, scissors, ...\n```\n\n### Repeat (Infinite)\nRepeat an item forever.\n\n```Infinite::repeat(mixed $item)```\n\n```php\nuse IterTools\\Infinite;\n\n$dialogue = 'Are we there yet?';\n\nforeach (Infinite::repeat($dialogue) as $repeated) {\n    print($repeated);\n}\n// 'Are we there yet?', 'Are we there yet?', 'Are we there yet?', ...\n```\n\n## Random Iteration\n### Choice\nGenerate random selections from an array of values.\n\n```Random::choice(array $items, int $repetitions)```\n\n```php\nuse IterTools\\Random;\n\n$cards       = ['Ace', 'King', 'Queen', 'Jack', 'Joker'];\n$repetitions = 10;\n\nforeach (Random::choice($cards, $repetitions) as $card) {\n    print($card);\n}\n// 'King', 'Jack', 'King', 'Ace', ... [random]\n```\n\n### CoinFlip\nGenerate random coin flips (0 or 1).\n\n```Random::coinFlip(int $repetitions)```\n\n```php\nuse IterTools\\Random;\n\n$repetitions = 10;\n\nforeach (Random::coinFlip($repetitions) as $coinFlip) {\n    print($coinFlip);\n}\n// 1, 0, 1, 1, 0, ... [random]\n```\n\n### Number\nGenerate random numbers (integers).\n\n```Random::number(int $min, int $max, int $repetitions)```\n\n```php\nuse IterTools\\Random;\n\n$min         = 1;\n$max         = 4;\n$repetitions = 10;\n\nforeach (Random::number($min, $max, $repetitions) as $number) {\n    print($number);\n}\n// 3, 2, 5, 5, 1, 2, ... [random]\n```\n\n### Percentage\nGenerate a random percentage between 0 and 1.\n\n```Random::percentage(int $repetitions)```\n\n```php\nuse IterTools\\Random;\n\n$repetitions = 10;\n\nforeach (Random::percentage($repetitions) as $percentage) {\n    print($percentage);\n}\n// 0.30205562629132, 0.59648594775233, ... [random]\n```\n\n### RockPaperScissors\nGenerate random rock-paper-scissors hands.\n\n```Random::rockPaperScissors(int $repetitions)```\n\n```php\nuse IterTools\\Random;\n\n$repetitions = 10;\n\nforeach (Random::rockPaperScissors($repetitions) as $rpsHand) {\n    print($rpsHand);\n}\n// 'paper', 'rock', 'rock', 'scissors', ... [random]\n```\n\n## Math Iteration\n### Frequencies\nReturns a frequency distribution of the data.\n\n```Math::frequencies(iterable $data, bool $strict = true): \\Generator```\n\nDefaults to [strict type](#Strict-and-Coercive-Types) comparisons. Set strict to false for type coercion comparisons.\n\n```php\nuse IterTools\\Math;\n\n$grades = ['A', 'A', 'B', 'B', 'B', 'C'];\n\nforeach (Math::frequencies($grades) as $grade =\u003e $frequency) {\n    print(\"$grade: $frequency\" . \\PHP_EOL);\n}\n// A: 2, B: 3, C: 1\n```\n\n### Relative Frequencies\nReturns a relative frequency distribution of the data.\n\n```Math::relativeFrequencies(iterable $data, bool $strict = true): \\Generator```\n\nDefaults to [strict type](#Strict-and-Coercive-Types) comparisons. Set strict to false for type coercion comparisons.\n\n```php\nuse IterTools\\Math;\n\n$grades = ['A', 'A', 'B', 'B', 'B', 'C'];\n\nforeach (Math::relativeFrequencies($grades) as $grade =\u003e $frequency) {\n    print(\"$grade: $frequency\" . \\PHP_EOL);\n}\n// A: 0.33, B: 0.5, C: 0.166\n```\n\n### Running Average\nAccumulate the running average over a list of numbers.\n\n```Math::runningAverage(iterable $numbers, int|float $initialValue = null)```\n\n```php\nuse IterTools\\Math;\n\n$grades = [100, 80, 80, 90, 85];\n\nforeach (Math::runningAverage($grades) as $runningAverage) {\n    print($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```Math::runningDifference(iterable $numbers, int|float $initialValue = null)```\n\n```php\nuse IterTools\\Math;\n\n$credits = [1, 2, 3, 4, 5];\n\nforeach (Math::runningDifference($credits) as $runningDifference) {\n    print($runningDifference);\n}\n// -1, -3, -6, -10, -15\n```\nProvide an optional initial value to lead off the running difference.\n```php\nuse IterTools\\Math;\n\n$dartsScores   = [50, 50, 25, 50];\n$startingScore = 501;\n\nforeach (Math::runningDifference($dartsScores, $startingScore) as $runningScore) {\n    print($runningScore);\n}\n// 501, 451, 401, 376, 326\n```\n\n### Running Max\nAccumulate the running maximum over a list of numbers.\n\n```Math::runningMax(iterable $numbers, int|float $initialValue = null)```\n\n```php\nuse IterTools\\Math;\n\n$numbers = [1, 2, 1, 3, 5];\n\nforeach (Math::runningMax($numbers) as $runningMax) {\n    print($runningMax);\n}\n// 1, 2, 2, 3, 5\n```\n\n### Running Min\nAccumulate the running minimum over a list of numbers.\n\n```Math::runningMin(iterable $numbers, int|float $initialValue = null)```\n\n```php\nuse IterTools\\Math;\n\n$numbers = [3, 4, 2, 5, 1];\n\nforeach (Math::runningMin($numbers) as $runningMin) {\n    print($runningMin);\n}\n// 3, 3, 2, 2, 1\n```\n\n### Running Product\nAccumulate the running product over a list of numbers.\n\n```Math::runningProduct(iterable $numbers, int|float $initialValue = null)```\n\n```php\nuse IterTools\\Math;\n\n$numbers = [1, 2, 3, 4, 5];\n\nforeach (Math::runningProduct($numbers) as $runningProduct) {\n    print($runningProduct);\n}\n// 1, 2, 6, 24, 120\n```\n\nProvide an optional initial value to lead off the running product.\n```php\nuse IterTools\\Math;\n\n$numbers      = [1, 2, 3, 4, 5];\n$initialValue = 5;\n\nforeach (Math::runningProduct($numbers, $initialValue) as $runningProduct) {\n    print($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```Math::runningTotal(iterable $numbers, int|float $initialValue = null)```\n\n```php\nuse IterTools\\Math;\n\n$prices = [1, 2, 3, 4, 5];\n\nforeach (Math::runningTotal($prices) as $runningTotal) {\n    print($runningTotal);\n}\n// 1, 3, 6, 10, 15\n```\n\nProvide an optional initial value to lead off the running total.\n```php\nuse IterTools\\Math;\n\n$prices       = [1, 2, 3, 4, 5];\n$initialValue = 5;\n\nforeach (Math::runningTotal($prices, $initialValue) as $runningTotal) {\n    print($runningTotal);\n}\n// 5, 6, 8, 11, 15, 20\n```\n\n## Set and Multiset\n### Distinct\nFilter out elements from the iterable only returning distinct elements.\n\n```Set::distinct(iterable $data, bool $strict = true)```\n\nDefaults to [strict type](#Strict-and-Coercive-Types) comparisons. Set strict to false for type coercion comparisons.\n\n```php\nuse IterTools\\Set;\n\n$chessSet = ['rook', 'rook', 'knight', 'knight', 'bishop', 'bishop', 'king', 'queen', 'pawn', 'pawn', ... ];\n\nforeach (Set::distinct($chessSet) as $chessPiece) {\n    print($chessPiece);\n}\n// rook, knight, bishop, king, queen, pawn\n\n$mixedTypes = [1, '1', 2, '2', 3];\n\nforeach (Set::distinct($mixedTypes, false) as $datum) {\n    print($datum);\n}\n// 1, 2, 3\n```\n\n### Distinct By\nFilter out elements from the iterable only returning distinct elements according to a custom comparator function.\n\n```Set::distinctBy(iterable $data, callable $compareBy)```\n\n```php\nuse IterTools\\Set;\n\n$streetFighterConsoleReleases = [\n    ['id' =\u003e '112233', 'name' =\u003e 'Street Fighter 3 3rd Strike', 'console' =\u003e 'Dreamcast'],\n    ['id' =\u003e '223344', 'name' =\u003e 'Street Fighter 3 3rd Strike', 'console' =\u003e 'PS4'],\n    ['id' =\u003e '334455', 'name' =\u003e 'Street Fighter 3 3rd Strike', 'console' =\u003e 'PS5'],\n    ['id' =\u003e '445566', 'name' =\u003e 'Street Fighter VI', 'console' =\u003e 'PS4'],\n    ['id' =\u003e '556677', 'name' =\u003e 'Street Fighter VI', 'console' =\u003e 'PS5'],\n    ['id' =\u003e '667788', 'name' =\u003e 'Street Fighter VI', 'console' =\u003e 'PC'],\n];\n$compareBy = fn ($sfTitle) =\u003e $sfTitle['name'];\n\n$uniqueTitles = [];\nforeach (Set::distinctBy($streetFighterConsoleReleases, $compareBy) as $sfTitle) {\n    $uniqueTitles[] = $sfTitle;\n}\n\n// Contains one SF3 3rd Strike entry and one SFVI entry.\n```\n\n### Intersection\nIterates intersection of iterables.\n\n```Set::intersection(iterable ...$iterables)```\n\nIf input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) intersection rules apply.\n\n```php\nuse IterTools\\Set;\n\n$chessPieces = ['rook', 'knight', 'bishop', 'queen', 'king', 'pawn'];\n$shogiPieces = ['rook', 'knight', 'bishop' 'king', 'pawn', 'lance', 'gold general', 'silver general'];\n\nforeach (Set::intersection($chessPieces, $shogiPieces) as $commonPiece) {\n    print($commonPiece);\n}\n// rook, knight, bishop, king, pawn\n```\n\n### Intersection Coercive\nIterates intersection of iterables using [type coercion](#Strict-and-Coercive-Types).\n\n```Set::intersectionCoercive(iterable ...$iterables)```\n\nIf input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) intersection rules apply.\n\n```php\nuse IterTools\\Set;\n\n$numbers  = [1, 2, 3, 4, 5];\n$numerics = ['1', '2', 3];\n\nforeach (Set::intersectionCoercive($numbers, $numerics) as $commonNumber) {\n    print($commonNumber);\n}\n// 1, 2, 3\n```\n\n### Partial Intersection\nIterates [M-partial intersection](https://github.com/Smoren/partial-intersection-php) of iterables.\n\n```Set::partialIntersection(int $minIntersectionCount, iterable ...$iterables)```\n\n* If input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) intersection rules apply.\n\n```php\nuse IterTools\\Set;\n\n$staticallyTyped    = ['c++', 'java', 'c#', 'go', 'haskell'];\n$dynamicallyTyped   = ['php', 'python', 'javascript', 'typescript'];\n$supportsInterfaces = ['php', 'java', 'c#', 'typescript'];\n\nforeach (Set::partialIntersection(2, $staticallyTyped, $dynamicallyTyped, $supportsInterfaces) as $language) {\n    print($language);\n}\n// c++, java, c#, go, php\n```\n\n### Partial Intersection Coercive\nIterates [M-partial intersection](https://github.com/Smoren/partial-intersection-php) of iterables using [type coercion](#Strict-and-Coercive-Types).\n\n```Set::partialIntersectionCoercive(int $minIntersectionCount, iterable ...$iterables)```\n\n* If input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) intersection rules apply.\n\n```php\nuse IterTools\\Set;\n\n$set1 = [1, 2, 3],\n$set2 = ['2', '3', 4, 5],\n$set3 = [1, '2'],\n\nforeach (Set::partialIntersectionCoercive(2, $set1, $set2, $set3) as $partiallyCommonNumber) {\n    print($partiallyCommonNumber);\n}\n// 1, 2, 3\n```\n\n### Symmetric difference\nIterates the symmetric difference of iterables.\n\n```Set::symmetricDifference(iterable ...$iterables)```\n\nIf input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) difference rules apply.\n\n```php\nuse IterTools\\Set;\n\n$a = [1, 2, 3, 4, 7];\n$b = ['1', 2, 3, 5, 8];\n$c = [1, 2, 3, 6, 9];\n\nforeach (Set::symmetricDifference($a, $b, $c) as $item) {\n    print($item);\n}\n// 1, 4, 5, 6, 7, 8, 9\n```\n\n### Symmetric difference Coercive\nIterates the symmetric difference of iterables with [type coercion](#Strict-and-Coercive-Types).\n\n```Set::symmetricDifferenceCoercive(iterable ...$iterables)```\n\nIf input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) difference rules apply.\n\n```php\nuse IterTools\\Set;\n\n$a = [1, 2, 3, 4, 7];\n$b = ['1', 2, 3, 5, 8];\n$c = [1, 2, 3, 6, 9];\n\nforeach (Set::symmetricDifferenceCoercive($a, $b, $c) as $item) {\n    print($item);\n}\n// 4, 5, 6, 7, 8, 9\n```\n\n### Union\nIterates the union of iterables.\n\n```Set::union(iterable ...$iterables)```\n\nIf input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) union rules apply.\n\n```php\nuse IterTools\\Set;\n\n$a = [1, 2, 3];\n$b = [3, 4];\n$c = [1, 2, 3, 6, 7];\n\nforeach (Set::union($a, $b, $c) as $item) {\n    print($item);\n}\n//1, 2, 3, 4, 6, 7\n```\n\n### Union Coercive\nIterates the union of iterables with [type coercion](#Strict-and-Coercive-Types).\n\n```Set::unionCoercive(iterable ...$iterables)```\n\nIf input iterables produce duplicate items, then [multiset](https://en.wikipedia.org/wiki/Multiset) union rules apply.\n\n```php\nuse IterTools\\Set;\n\n$a = ['1', 2, 3];\n$b = [3, 4];\n$c = [1, 2, 3, 6, 7];\n\nforeach (Set::unionCoercive($a, $b, $c) as $item) {\n    print($item);\n}\n//1, 2, 3, 4, 6, 7\n```\n\n## Sort Iteration\n### ASort\nIterate the collection sorted while maintaining the associative key index relations.\n\n```Sort::sort(iterable $data, callable $comparator = null)```\n\nUses default sorting if optional comparator function not provided.\n\n```php\nuse IterTools\\Single;\n\n$worldPopulations = [\n    'China'     =\u003e 1_439_323_776,\n    'India'     =\u003e 1_380_004_385,\n    'Indonesia' =\u003e 273_523_615,\n    'Pakistan'  =\u003e 220_892_340,\n    'USA'       =\u003e 331_002_651,\n];\n\nforeach (Sort::sort($worldPopulations) as $country =\u003e $population) {\n    print(\"$country: $population\" . \\PHP_EOL);\n}\n// Pakistan: 220,892,340\n// Indonesia: 273,523,615\n// USA: 331,002,651\n// India: 1,380,004,385\n// China: 1,439,323,776\n```\n\n### Sort\nIterate the collection sorted.\n\n```Sort::sort(iterable $data, callable $comparator = null)```\n\nUses default sorting if optional comparator function not provided.\n\n```php\nuse IterTools\\Single;\n\n$data = [3, 4, 5, 9, 8, 7, 1, 6, 2];\n\nforeach (Sort::sort($data) as $datum) {\n    print($datum);\n}\n// 1, 2, 3, 4, 5, 6, 7, 8, 9\n```\n\n## File\n### Read CSV\nIterate the lines of a CSV file.\n\n```File::readCsv(resource $fileHandle, string $separator = ',', string $enclosure = '\"', string $escape = '\\\\')```\n\n```php\nuse IterTools\\File;\n\n$fileHandle = \\fopen('path/to/file.csv', 'r');\n\nforeach (File::readCsv($fileHandle) as $row) {\n    print_r($row);\n}\n// Each column field is an element of the array\n```\n\n### Read Lines\nIterate the lines of a file.\n\n```File::readLines(resource $fileHandle)```\n\n```php\nuse IterTools\\File;\n\n$fileHandle = \\fopen('path/to/file.txt', 'r');\n\nforeach (File::readLines($fileHandle) as $line) {\n    print($line);\n}\n```\n\n## Transform\n### Tee\nReturn several independent (duplicated) iterators from a single iterable.\n\n```Transform::tee(iterable $data, int $count): array```\n\n```php\nuse IterTools\\Transform;\n\n$daysOfWeek = ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun'];\n$count = 3;\n\n[$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\nTransforms any iterable to an array.\n\n```Transform::toArray(iterable $data): array```\n\n```php\nuse IterTools\\Transform;\n\n$iterator = new \\ArrayIterator([1, 2, 3, 4, 5]);\n\n$array = Transform::toArray($iterator);\n```\n\n### To Associative Array\nTransforms any iterable to an associative array.\n\n```Transform::toAssociativeArray(iterable $data, callable $keyFunc = null, callable $valueFunc = null): array```\n\n```php\nuse IterTools\\Transform;\n\n$messages = ['message 1', 'message 2', 'message 3'];\n\n$keyFunc   = fn ($msg) =\u003e \\md5($msg);\n$valueFunc = fn ($msg) =\u003e strtoupper($msg);\n\n$associativeArray = Transform::toAssociativeArray($messages, $keyFunc, $valueFunc);\n// [\n//     '1db65a6a0a818fd39655b95e33ada11d' =\u003e 'MESSAGE 1',\n//     '83b2330607fe8f817ce6d24249dea373' =\u003e 'MESSAGE 2',\n//     '037805d3ad7b10c5b8425427b516b5ce' =\u003e 'MESSAGE 3',\n// ]\n```\n\n### To Iterator\nTransforms any iterable to an iterator.\n\n```Transform::toArray(iterable $data): array```\n\n```php\nuse IterTools\\Transform;\n\n$array = [1, 2, 3, 4, 5];\n\n$iterator = Transform::toIterator($array);\n```\n\n## Summary\n### All Match\nReturns true if all elements match the predicate function.\n\n```Summary::allMatch(iterable $data, callable $predicate): bool```\n\n```php\nuse IterTools\\Summary;\n\n$finalFantasyNumbers = [4, 5, 6];\n$isOnSuperNintendo   = fn ($ff) =\u003e $ff \u003e= 4 \u0026\u0026 $ff \u003c= 6;\n\n$boolean = Summary::allMatch($finalFantasyNumbers, $isOnSuperNintendo);\n// true\n\n$isOnPlaystation = fn ($ff) =\u003e $ff \u003e= 7 \u0026\u0026 $ff \u003c= 9;\n\n$boolean = Summary::allMatch($finalFantasyNumbers, $isOnPlaystation);\n// false\n```\n\n### All Unique\nReturns true if all elements are unique.\n\n```Summary::allUnique(iterable $data, bool $strict = true): bool```\n\nDefaults to [strict type](#Strict-and-Coercive-Types) comparisons. Set strict to false for type coercion comparisons.\n\n```php\nuse IterTools\\Summary;\n\n$items = ['fingerprints', 'snowflakes', 'eyes', 'DNA']\n\n$boolean = Summary::allUnique($items);\n// true\n```\n\n### Any Match\nReturns true if any element matches the predicate function.\n\n```Summary::anyMatch(iterable $data, callable $predicate): bool```\n\n```php\nuse IterTools\\Summary;\n\n$answers          = ['fish', 'towel', 42, \"don't panic\"];\n$isUltimateAnswer = fn ($a) =\u003e a == 42;\n\n$boolean = Summary::anyMatch($answers, $isUltimateAnswer);\n// true\n```\n\n### Are Permutations\nReturns true if all iterables are permutations of each other.\n\n```Summary::arePermutations(iterable ...$iterables): bool```\n\n```php\nuse IterTools\\Summary;\n\n$iter = ['i', 't', 'e', 'r'];\n$rite = ['r', 'i', 't', 'e'];\n$reit = ['r', 'e', 'i', 't'];\n$tier = ['t', 'i', 'e', 'r'];\n$tire = ['t', 'i', 'r', 'e'];\n$trie = ['t', 'r', 'i', 'e'];\n\n$boolean = Summary::arePermutations($iter, $rite, $reit, $tier, $tire, $trie);\n// true\n```\n\n### Are Permutations Coercive\nReturns true if all iterables are permutations of each other with [type coercion](#Strict-and-Coercive-Types).\n\n```Summary::arePermutationsCoercive(iterable ...$iterables): bool```\n\n```php\nuse IterTools\\Summary;\n\n$set1 = [1, 2.0, '3'];\n$set2 = [2.0, '1', 3];\n$set3 = [3, 2, 1];\n\n$boolean = Summary::arePermutationsCoercive($set1, $set2, $set3);\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```Summary::exactlyN(iterable $data, int $n, callable $predicate): bool```\n\n```php\nuse IterTools\\Summary;\n\n$twoTruthsAndALie = [true, true, false];\n$n                = 2;\n\n$boolean = Summary::exactlyN($twoTruthsAndALie, $n);\n// true\n\n$ages      = [18, 21, 24, 54];\n$n         = 4;\n$predicate = fn ($age) =\u003e $age \u003e= 21;\n\n$boolean = Summary::exactlyN($ages, $n, $predicate);\n// false\n```\n\n### Is Empty\nReturns true if the iterable is empty having no items.\n\n```Summary::isEmpty(iterable $data): bool```\n\n```php\nuse IterTools\\Summary;\n\n$data = []\n\n$boolean = Summary::isEmpty($data);\n// true\n```\n\n### Is Partitioned\nReturns true if all elements of given collection that satisfy the predicate appear before all elements that don't.\n\n- Returns true for empty collection or for collection with single item.\n- Default predicate if not provided is the boolean value of each data item.\n\n```Summary::isPartitioned(iterable $data, callable $predicate = null): bool```\n\n```php\nuse IterTools\\Summary;\n\n$numbers          = [0, 2, 4, 1, 3, 5];\n$evensBeforeOdds = fn ($item) =\u003e $item % 2 === 0;\n\n$boolean = Summary::isPartitioned($numbers, $evensBeforeOdds);\n```\n\n### Is Sorted\nReturns true if elements are sorted, otherwise false.\n\n- Elements must be comparable.\n- Returns true if empty or has only one element.\n\n```Summary::isSorted(iterable $data): bool```\n\n```php\nuse IterTools\\Summary;\n\n$numbers = [1, 2, 3, 4, 5];\n\n$boolean = Summary::isSorted($numbers);\n// true\n\n$numbers = [3, 2, 3, 4, 5];\n\n$boolean = Summary::isSorted($numbers);\n// false\n```\n\n### Is Reversed\nReturns true if elements are reverse sorted, otherwise false.\n\n- Elements must be comparable.\n- Returns true if empty or has only one element.\n\n```Summary::isReversed(iterable $data): bool```\n\n```php\nuse IterTools\\Summary;\n\n$numbers = [5, 4, 3, 2, 1];\n\n$boolean = Summary::isReversed($numbers);\n// true\n\n$numbers = [1, 4, 3, 2, 1];\n\n$boolean = Summary::isReversed($numbers);\n// false\n```\n\n### None Match\nReturns true if no element matches the predicate function.\n\n```Summary::noneMatch(iterable $data, callable $predicate): bool```\n\n```php\nuse IterTools\\Summary;\n\n$grades         = [45, 50, 61, 0];\n$isPassingGrade = fn ($grade) =\u003e $grade \u003e= 70;\n\n$boolean = Summary::noneMatch($grades, $isPassingGrade);\n// true\n```\n\n### Same\nReturns true if all given collections are the same.\n\nFor single iterable or empty iterables list returns true.\n\n```Summary::same(iterable ...$iterables): bool```\n\n```php\nuse IterTools\\Summary;\n\n$cocaColaIngredients = ['carbonated water', 'sugar', 'caramel color', 'phosphoric acid'];\n$pepsiIngredients    = ['carbonated water', 'sugar', 'caramel color', 'phosphoric acid'];\n\n$boolean = Summary::same($cocaColaIngredients, $pepsiIngredients);\n// true\n\n$cocaColaIngredients = ['carbonated water', 'sugar', 'caramel color', 'phosphoric acid'];\n$spriteIngredients   = ['carbonated water', 'sugar', 'citric acid', 'lemon lime flavorings'];\n\n$boolean = Summary::same($cocaColaIngredients, $spriteIngredients);\n// false\n```\n\n### Same Count\nReturns true if all given collections have the same lengths.\n\nFor single iterable or empty iterables list returns true.\n\n```Summary::sameCount(iterable ...$iterables): bool```\n\n```php\nuse IterTools\\Summary;\n\n$prequels  = ['Phantom Menace', 'Attack of the Clones', 'Revenge of the Sith'];\n$originals = ['A New Hope', 'Empire Strikes Back', 'Return of the Jedi'];\n$sequels   = ['The Force Awakens', 'The Last Jedi', 'The Rise of Skywalker'];\n\n$boolean = Summary::sameCount($prequels, $originals, $sequels);\n// true\n\n$batmanMovies = ['Batman Begins', 'The Dark Knight', 'The Dark Knight Rises'];\n$matrixMovies = ['The Matrix', 'The Matrix Reloaded', 'The Matrix Revolutions', 'The Matrix Resurrections'];\n\n$result = Summary::sameCount($batmanMovies, $matrixMovies);\n// false\n```\n\n## Reduce\n### To Average\nReduces to the mean average.\n\nReturns null if collection is empty.\n\n```Reduce::toAverage(iterable $data): float```\n\n```php\nuse IterTools\\Reduce;\n\n$grades = [100, 90, 95, 85, 94];\n\n$finalGrade = Reduce::toAverage($numbers);\n// 92.8\n```\n\n### To Count\nReduces iterable to its length.\n\n```Reduce::toCount(iterable $data): int```\n\n```php\nuse IterTools\\Reduce;\n\n$someIterable = ImportantThing::getCollectionAsIterable();\n\n$length = Reduce::toCount($someIterable);\n// 3\n```\n\n### To First\nReduces iterable to its first element.\n\n```Reduce::toFirst(iterable $data): mixed```\n\nThrows `\\LengthException` if collection is empty.\n\n```php\nuse IterTools\\Reduce;\n\n$medals = ['gold', 'silver', 'bronze'];\n\n$first = Reduce::toFirst($medals);\n// gold\n```\n\n### To First And Last\nReduces iterable to its first and last elements.\n\n```Reduce::toFirstAndLast(iterable $data): array{mixed, mixed}```\n\nThrows `\\LengthException` if collection is empty.\n\n```php\nuse IterTools\\Reduce;\n\n$weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'];\n\n$firstAndLast = Reduce::toFirstAndLast($weekdays);\n// [Monday, Friday]\n```\n\n### To Last\nReduces iterable to its last element.\n\n```Reduce::toLast(iterable $data): mixed```\n\nThrows `\\LengthException` if collection is empty.\n\n```php\nuse IterTools\\Reduce;\n\n$gnomesThreePhasePlan = ['Collect underpants', '?', 'Profit'];\n\n$lastPhase = Reduce::toLast($gnomesThreePhasePlan);\n// Profit\n```\n\n### To Max\nReduces to the max value.\n\n```Reduce::toMax(iterable $data, callable $compareBy = null): mixed|null```\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 null if collection is empty.\n\n```php\nuse IterTools\\Reduce;\n\n$numbers = [5, 3, 1, 2, 4];\n\n$result = Reduce::toMax($numbers);\n// 5\n\n$movieRatings = [\n    [\n        'title' =\u003e 'Star Wars: Episode IV - A New Hope',\n        'rating' =\u003e 4.6\n    ],\n    [\n        'title' =\u003e 'Star Wars: Episode V - The Empire Strikes Back',\n        'rating' =\u003e 4.8\n    ],\n    [\n        'title' =\u003e 'Star Wars: Episode VI - Return of the Jedi',\n        'rating' =\u003e 4.6\n    ],\n];\n$compareBy = fn ($movie) =\u003e $movie['rating'];\n\n$highestRatedMovie = Reduce::toMax($movieRatings, $compareBy);\n// [\n//     'title' =\u003e 'Star Wars: Episode V - The Empire Strikes Back',\n//     'rating' =\u003e 4.8\n// ];\n```\n\n### To Min\nReduces to the min value.\n\n```Reduce::toMin(iterable $data, callable $compareBy = null): mixed|null```\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 null if collection is empty.\n\n```php\nuse IterTools\\Reduce;\n\n$numbers = [5, 3, 1, 2, 4];\n\n$result = Reduce::toMin($numbers);\n// 1\n\n\n$movieRatings = [\n    [\n        'title' =\u003e 'The Matrix',\n        'rating' =\u003e 4.7\n    ],\n    [\n        'title' =\u003e 'The Matrix Reloaded',\n        'rating' =\u003e 4.3\n    ],\n    [\n        'title' =\u003e 'The Matrix Revolutions',\n        'rating' =\u003e 3.9\n    ],\n    [\n        'title' =\u003e 'The Matrix Resurrections',\n        'rating' =\u003e 2.5\n    ],\n];\n$compareBy = fn ($movie) =\u003e $movie['rating'];\n\n$lowestRatedMovie = Reduce::toMin($movieRatings, $compareBy);\n// [\n//     'title' =\u003e 'The Matrix Resurrections',\n//     'rating' =\u003e 2.5\n// ]\n```\n\n### To Min Max\nReduces to array of its upper and lower bounds (max and min).\n\n```Reduce::toMinMax(iterable $numbers, callable $compareBy = null): array```\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 `[null, null]` if given collection is empty.\n\n```php\nuse IterTools\\Reduce;\n\n$numbers = [1, 2, 7, -1, -2, -3];\n\n[$min, $max] = Reduce::toMinMax($numbers);\n// [-3, 7]\n\n$reportCard = [\n    [\n        'subject' =\u003e 'history',\n        'grade' =\u003e 90\n    ],\n    [\n        'subject' =\u003e 'math',\n        'grade' =\u003e 98\n    ],\n    [\n        'subject' =\u003e 'science',\n        'grade' =\u003e 92\n    ],\n    [\n        'subject' =\u003e 'english',\n        'grade' =\u003e 85\n    ],\n    [\n        'subject' =\u003e 'programming',\n        'grade' =\u003e 100\n    ],\n];\n$compareBy = fn ($class) =\u003e $class['grade'];\n\n$bestAndWorstSubject = Reduce::toMinMax($reportCard, $compareBy);\n// [\n//     [\n//         'subject' =\u003e 'english',\n//         'grade' =\u003e 85\n//     ],\n//     [\n//         'subject' =\u003e 'programming',\n//         'grade' =\u003e 100\n//     ],\n// ]\n```\n\n### To Nth\nReduces to value at the nth position.\n\n```Reduce::toNth(iterable $data, int $position): mixed```\n\n```php\nuse IterTools\\Reduce;\n\n$lotrMovies = ['The Fellowship of the Ring', 'The Two Towers', 'The Return of the King'];\n\n$rotk = Reduce::toNth($lotrMovies, 2);\n// 20\n```\n\n### To Product\nReduces to the product of its elements.\n\nReturns null if collection is empty.\n\n```Reduce::toProduct(iterable $data): number|null```\n\n```php\nuse IterTools\\Reduce;\n\n$primeFactors = [5, 2, 2];\n\n$number = Reduce::toProduct($primeFactors);\n// 20\n```\n\n### To Random Value\nReduces given collection to a random value within it.\n\n```Reduce::toRandomValue(iterable $data): mixed```\n\n```php\nuse IterTools\\Reduce;\n\n$sfWakeupOptions = ['mid', 'low', 'overhead', 'throw', 'meaty'];\n\n$wakeupOption = Reduce::toRandomValue($sfWakeupOptions);\n// e.g., throw\n```\n\n### To Range\nReduces given collection to its range (difference between max and min).\n\n```Reduce::toRange(iterable $numbers): int|float```\n\nReturns `0` if iterable source is empty.\n\n```php\nuse IterTools\\Reduce;\n\n$grades = [100, 90, 80, 85, 95];\n\n$range = Reduce::toRange($numbers);\n// 20\n```\n\n### To String\nReduces to a string joining all elements.\n\n* Optional separator to insert between items.\n* Optional prefix to prepend to the string.\n* Optional suffix to append to the string.\n\n```Reduce::toString(iterable $data, string $separator = '', string $prefix = '', string $suffix = ''): string```\n\n```php\nuse IterTools\\Reduce;\n\n$words = ['IterTools', 'PHP', 'v1.0'];\n\n$string = Reduce::toString($words);\n// IterToolsPHPv1.0\n$string = Reduce::toString($words, '-');\n// IterTools-PHP-v1.0\n$string = Reduce::toString($words, '-', 'Library: ');\n// Library: IterTools-PHP-v1.0\n$string = Reduce::toString($words, '-', 'Library: ', '!');\n// Library: IterTools-PHP-v1.0!\n```\n\n### To Sum\nReduces to the sum of its elements.\n\n```Reduce::toSum(iterable $data): number```\n\n```php\nuse IterTools\\Reduce;\n\n$parts = [10, 20, 30];\n\n$sum = Reduce::toSum($parts);\n// 60\n```\n\n### To Value\nReduce elements to a single value using reducer function.\n\n```Reduce::toValue(iterable $data, callable $reducer, mixed $initialValue): mixed```\n\n```php\nuse IterTools\\Reduce;\n\n$input = [1, 2, 3, 4, 5];\n$sum   = fn ($carry, $item) =\u003e $carry + $item;\n\n$result = Reduce::toValue($input, $sum, 0);\n// 15\n```\n\n## Stream\n\nStreams provide a fluent interface to transform arrays and iterables 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   ```php\n   $result = Stream::of([1, 1, 2, 2, 3, 4, 5])\n      -\u003edistinct()                  // [1, 2, 3, 4, 5]\n      -\u003emap(fn ($x) =\u003e $x**2)       // [1, 4, 9, 16, 25]\n      -\u003efilter(fn ($x) =\u003e $x \u003c 10)  // [1, 4, 9]\n      -\u003etoSum();                    // 14\n   ```\n   * The stream is iterated via a `foreach` loop.\n   ```php\n   $result = Stream::of([1, 1, 2, 2, 3, 4, 5])\n      -\u003edistinct()                  // [1, 2, 3, 4, 5]\n      -\u003emap(fn ($x) =\u003e $x**2)       // [1, 4, 9, 16, 25]\n      -\u003efilter(fn ($x) =\u003e $x \u003c 10); // [1, 4, 9]\n\n   foreach ($result as $item) {\n       // 1, 4, 9\n   }\n   ```\n\n### Stream Sources\n\n#### Of\nCreates stream from an iterable.\n\n```Stream::of(iterable $iterable): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$iterable = [1, 2, 3];\n\n$result = Stream::of($iterable)\n    -\u003echainWith([4, 5, 6], [7, 8, 9])\n    -\u003ezipEqualWith([1, 2, 3, 4, 5, 6, 7, 8, 9])\n    -\u003etoValue(fn ($carry, $item) =\u003e $carry + array_sum($item));\n// 90\n```\n\n#### Of Coin Flips\nCreates stream of n random coin flips.\n\n```Stream::ofCoinFlips(int $repetitions): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$result = Stream::ofCoinFlips(10)\n    -\u003efilterTrue()\n    -\u003etoCount();\n// 5 (random)\n```\n\n#### Of CSV File\nCreates a stream of rows of a CSV file.\n\n```Stream::ofCsvFile(resource $fileHandle, string $separator = ',', string $enclosure = '\"', string = $escape = '\\\\'): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$fileHandle = \\fopen('path/to/file.csv', 'r');\n\n$result = Stream::of($fileHandle)\n    -\u003etoArray();\n```\n\n#### Of Empty\nCreates stream of nothing.\n\n```Stream::ofEmpty(): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$result = Stream::ofEmpty()\n    -\u003echainWith([1, 2, 3])\n    -\u003etoArray();\n// 1, 2, 3\n```\n\n#### Of File Lines\nCreates a stream of lines of a file.\n\n```Stream::ofFileLines(resource $fileHandle): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$fileHandle = \\fopen('path/to/file.txt', 'r');\n\n$result = Stream::of($fileHandle)\n    -\u003emap('strtoupper');\n    -\u003etoArray();\n```\n\n#### Of Random Choice\nCreates stream of random selections from an array of values.\n\n```Stream::ofRandomChoice(array $items, int $repetitions): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$languages = ['PHP', 'Go', 'Python'];\n\n$languages = Stream::ofRandomChoice($languages, 5)\n    -\u003etoArray();\n// 'Go', 'PHP', 'Python', 'PHP', 'PHP' (random)\n```\n\n#### Of Random Numbers\nCreates stream of random numbers (integers).\n\n```Stream::ofRandomNumbers(int $min, int $max, int $repetitions): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$min  = 1;\n$max  = 3;\n$reps = 7;\n\n$result = Stream::ofRandomNumbers($min, $max, $reps)\n    -\u003etoArray();\n// 1, 2, 2, 1, 3, 2, 1 (random)\n```\n\n#### Of Random Percentage\nCreates stream of random percentages between 0 and 1.\n\n```Stream::ofRandomPercentage(int $repetitions): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$stream = Stream::ofRandomPercentage(3)\n    -\u003etoArray();\n// 0.8012566976245, 0.81237281724151, 0.61676896329459 [random]\n```\n\n#### Of Range\nCreates stream of a range of numbers.\n\n```Stream::ofRange(int|float $start, int|float $end, int|float $step = 1): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$numbers = Stream::ofRange(0, 5)\n    -\u003etoArray();\n// 0, 1, 2, 3, 4, 5\n```\n\n#### Of Rock Paper Scissors\nCreates stream of rock-paper-scissors hands.\n\n```Stream::ofRockPaperScissors(int $repetitions): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$rps = Stream::ofRockPaperScissors(5)\n    -\u003etoArray();\n// 'paper', 'rock', 'rock', 'scissors', 'paper' [random]\n```\n\n### Stream Operations\n\n#### ASort\nSorts the stream, maintaining keys.\n\n```$stream-\u003easort(callable $comparator = null)```\n\nIf comparator is not provided, the elements of the iterable source must be comparable.\n\n```php\nuse IterTools\\Stream;\n\n$worldPopulations = [\n    'China'     =\u003e 1_439_323_776,\n    'India'     =\u003e 1_380_004_385,\n    'Indonesia' =\u003e 273_523_615,\n    'USA'       =\u003e 331_002_651,\n];\n\n$result = Stream::of($worldPopulations)\n    -\u003efilter(fn ($pop) =\u003e $pop \u003e 300_000_000)\n    -\u003easort()\n    -\u003etoAssociativeArray();\n// USA   =\u003e 331_002_651,\n// India =\u003e 1_380_004_385,\n// China =\u003e 1_439_323_776,\n```\n\n#### Chain With\nReturn a stream chaining additional sources together into a single consecutive stream.\n\n```$stream-\u003echainWith(iterable ...$iterables): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$input = [1, 2, 3];\n\n$result = Stream::of($input)\n    -\u003echainWith([4, 5, 6])\n    -\u003echainWith([7, 8, 9])\n    -\u003etoArray();\n// 1, 2, 3, 4, 5, 6, 7, 8, 9\n```\n\n#### Compress\nCompress to a new stream by filtering out data that is not selected.\n\n```$stream-\u003ecompress(iterable $selectors): Stream```\n\nSelectors indicate which data. True value selects item. False value filters out data.\n\n```php\nuse IterTools\\Stream;\n\n$input = [1, 2, 3];\n\n$result = Stream::of($input)\n    -\u003ecompress([0, 1, 1])\n    -\u003etoArray();\n// 2, 3\n```\n\n#### Compress Associative\nCompress to a new stream by filtering out keys that are not selected.\n\n```$stream-\u003ecompressAssociative(array $keys): Stream```\n\n* Standard PHP array/iterator keys only (string, integer).\n\n```php\nuse IterTools\\Stream;\n\n$starWarsEpisodes = [\n    'I'    =\u003e 'The Phantom Menace',\n    'II'   =\u003e 'Attack of the Clones',\n    'III'  =\u003e 'Revenge of the Sith',\n    'IV'   =\u003e 'A New Hope',\n    'V'    =\u003e 'The Empire Strikes Back',\n    'VI'   =\u003e 'Return of the Jedi',\n    'VII'  =\u003e 'The Force Awakens',\n    'VIII' =\u003e 'The Last Jedi',\n    'IX'   =\u003e 'The Rise of Skywalker',\n];\n$sequelTrilogyNumbers = ['VII', 'VIII', 'IX'];\n\n$sequelTrilogy = Stream::of($starWarsEpisodes)\n    -\u003ecompressAssociative($sequelTrilogyNumbers)\n    -\u003etoAssociativeArray();\n// 'VII'  =\u003e 'The Force Awakens',\n// 'VIII' =\u003e 'The Last Jedi',\n// 'IX'   =\u003e 'The Rise of Skywalker',\n```\n\n#### Chunkwise\nReturn a stream consisting of chunks of elements from the stream.\n\n```$stream-\u003echunkwise(int $chunkSize): Stream```\n\nChunk size must be at least 1.\n\n```php\nuse IterTools\\Stream;\n\n$friends = ['Ross', 'Rachel', 'Chandler', 'Monica', 'Joey'];\n\n$result = Stream::of($friends)\n    -\u003echunkwise(2)\n    -\u003etoArray();\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```$stream-\u003echunkwiseOverlap(int $chunkSize, int $overlapSize, bool $includeIncompleteTail = true): Stream```\n\n* Chunk size must be at least 1.\n* Overlap size must be less than chunk size.\n\n```php\nuse IterTools\\Stream;\n\n$numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];\n\n$result = Stream::of($friends)\n    -\u003echunkwiseOverlap(3, 1)\n    -\u003etoArray()\n// [1, 2, 3], [3, 4, 5], [5, 6, 7], [7, 8, 9]\n```\n\n#### Distinct\nReturn a stream filtering out elements from the stream only returning distinct elements.\n\n```$stream-\u003edistinct(bool $strict = true): Stream```\n\nDefaults to [strict type](#Strict-and-Coercive-Types) comparisons. Set strict to false for type coercion comparisons.\n\n```php\nuse IterTools\\Stream;\n\n$input = [1, 2, 1, 2, 3, 3, '1', '1', '2', '3'];\n$stream = Stream::of($input)\n    -\u003edistinct()\n    -\u003etoArray();\n// 1, 2, 3, '1', '2', '3'\n\n$stream = Stream::of($input)\n    -\u003edistinct(false)\n    -\u003etoArray();\n// 1, 2, 3\n```\n\n#### Distinct By\nReturn a stream filtering out elements from the stream only returning distinct elements according to a custom comparator function.\n\n```$stream-\u003edistinctBy(callable $compareBy): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$streetFighterConsoleReleases = [\n    ['id' =\u003e '112233', 'name' =\u003e 'Street Fighter 3 3rd Strike', 'console' =\u003e 'Dreamcast'],\n    ['id' =\u003e '223344', 'name' =\u003e 'Street Fighter 3 3rd Strike', 'console' =\u003e 'PS4'],\n    ['id' =\u003e '334455', 'name' =\u003e 'Street Fighter 3 3rd Strike', 'console' =\u003e 'PS5'],\n    ['id' =\u003e '445566', 'name' =\u003e 'Street Fighter VI', 'console' =\u003e 'PS4'],\n    ['id' =\u003e '556677', 'name' =\u003e 'Street Fighter VI', 'console' =\u003e 'PS5'],\n    ['id' =\u003e '667799', 'name' =\u003e 'Street Fighter VI', 'console' =\u003e 'PC'],\n];\n$stream = Stream::of($streetFighterConsoleReleases)\n    -\u003edistinctBy(fn ($sfTitle) =\u003e $sfTitle['name'])\n    -\u003etoArray();\n// Contains one SF3 3rd Strike entry and one SFVI entry\n```\n\n#### Drop While\nDrop elements from the stream while the predicate function is true.\n\n```$stream-\u003edropWhile(callable $predicate): Stream```\n\nOnce the predicate function returns false once, all remaining elements are returned.\n\n```php\nuse IterTools\\Stream;\n\n$input = [1, 2, 3, 4, 5]\n\n$result = Stream::of($input)\n    -\u003edropWhile(fn ($value) =\u003e $value \u003c 3)\n    -\u003etoArray();\n// 3, 4, 5\n```\n\n#### Filter\nFilter out elements from the stream only keeping elements where there predicate function is true.\n\n```$stream-\u003efilter(callable $predicate): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$input = [1, -1, 2, -2, 3, -3];\n\n$result = Stream::of($input)\n    -\u003efilter(fn ($value) =\u003e $value \u003e 0)\n    -\u003etoArray();\n// 1, 2, 3\n```\n\n#### Filter True\nFilter out elements from the stream only keeping elements that are truthy.\n\n```$stream-\u003efilterTrue(): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$input = [0, 1, 2, 3, 0, 4];\n\n$result = Stream::of($input)\n    -\u003efilterTrue()\n    -\u003etoArray();\n// 1, 2, 3, 4\n```\n\n#### Filter False\nFilter out elements from the stream only keeping elements that are falsy.\n\n```$stream-\u003efilterFalse(): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$input = [0, 1, 2, 3, 0, 4];\n\n$result = Stream::of($input)\n    -\u003efilterFalse()\n    -\u003etoArray();\n// 0, 0\n```\n\n#### Filter Keys\nFilter out elements from stream only keeping elements where the predicate function on the keys are true.\n\n```$stream-\u003efilterKeys(callable $filter): Stream```\n\n```php\n$olympics = [\n    2000 =\u003e 'Sydney',\n    2002 =\u003e 'Salt Lake City',\n    2004 =\u003e 'Athens',\n    2006 =\u003e 'Turin',\n    2008 =\u003e 'Beijing',\n    2010 =\u003e 'Vancouver',\n    2012 =\u003e 'London',\n    2014 =\u003e 'Sochi',\n    2016 =\u003e 'Rio de Janeiro',\n    2018 =\u003e 'Pyeongchang',\n    2020 =\u003e 'Tokyo',\n    2022 =\u003e 'Beijing',\n];\n\n$winterFilter = fn ($year) =\u003e $year % 4 === 2;\n\n$result = Stream::of($olympics)\n    -\u003efilterKeys($winterFilter)\n    -\u003etoAssociativeArray();\n}\n// 2002 =\u003e Salt Lake City\n// 2006 =\u003e Turin\n// 2010 =\u003e Vancouver\n// 2014 =\u003e Sochi\n// 2018 =\u003e Pyeongchang\n// 2022 =\u003e Beijing\n```\n\n#### Flat Map\nMap a function onto the elements of the stream and flatten the results.\n\n```$stream-\u003eflatMap(callable $mapper): Stream```\n\n```php\n$data    = [1, 2, 3, 4, 5];\n$mapper  fn ($item) =\u003e ($item % 2 === 0) ? [$item, $item] : $item;\n\n$result = Stream::of($data)\n    -\u003eflatMap($mapper)\n    -\u003etoArray();\n// [1, 2, 2, 3, 4, 4, 5]\n```\n\n#### Flatten\nFlatten a multidimensional stream.\n\n```$stream-\u003eflatten(int $dimensions = 1): Stream```\n\n```php\n$data = [1, [2, 3], [4, 5]];\n\n$result = Stream::of($data)\n    -\u003eflatten($mapper)\n    -\u003etoArray();\n// [1, 2, 3, 4, 5]\n```\n\n#### Frequencies\nFrequency distribution of the stream elements.\n\n```$stream-\u003efrequencies(bool $strict = true): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$grades = ['A', 'A', 'B', 'B', 'B', 'C'];\n\n$result = Stream::of($grades)\n    -\u003efrequencies()\n    -\u003etoAssociativeArray();\n\n// ['A' =\u003e 2, 'B' =\u003e 3, 'C' =\u003e 1]\n```\n\n#### Group By\nReturn a stream grouping by a common data element.\n\n```$stream-\u003egroupBy(callable $groupKeyFunction, callable $itemKeyFunction = null): Stream```\n\n* The `$groupKeyFunction` determines the key to group elements by.\n* The optional `$itemKeyFunction` allows custom indexes within each group member.\n\n```php\nuse IterTools\\Stream;\n\n$input = [1, -1, 2, -2, 3, -3];\n\n$groups = Stream::of($input)\n    -\u003egroupBy(fn ($item) =\u003e $item \u003e 0 ? 'positive' : 'negative');\n\nforeach ($groups as $group =\u003e $item) {\n    // 'positive' =\u003e [1, 2, 3], 'negative' =\u003e [-1, -2, -3]\n}\n```\n\n#### Infinite Cycle\nReturn a stream cycling through the elements of stream sequentially forever.\n\n```$stream-\u003einfiniteCycle(): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$input = [1, 2, 3];\n\n$result = Stream::of($input)\n    -\u003einfiniteCycle()\n    -\u003eprint();\n// 1, 2, 3, 1, 2, 3, ...\n```\n\n#### Intersection With\nReturn a stream intersecting the stream with the input iterables.\n\n```$stream-\u003eintersectionWith(iterable ...$iterables): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$numbers    = [1, 2, 3, 4, 5, 6, 7, 8, 9];\n$numerics   = ['1', '2', 3, 4, 5, 6, 7, '8', '9'];\n$oddNumbers = [1, 3, 5, 7, 9, 11];\n\n$stream = Stream::of($numbers)\n    -\u003eintersectionWith($numerics, $oddNumbers)\n    -\u003etoArray();\n// 3, 5, 7\n```\n\n#### Intersection Coercive With\nReturn a stream intersecting the stream with the input iterables using [type coercion](#Strict-and-Coercive-Types).\n\n```$stream-\u003eintersectionCoerciveWith(iterable ...$iterables): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$languages          = ['php', 'python', 'c++', 'java', 'c#', 'javascript', 'typescript'];\n$scriptLanguages    = ['php', 'python', 'javascript', 'typescript'];\n$supportsInterfaces = ['php', 'java', 'c#', 'typescript'];\n\n$stream = Stream::of($languages)\n    -\u003eintersectionCoerciveWith($scriptLanguages, $supportsInterfaces)\n    -\u003etoArray();\n// 'php', 'typescript'\n```\n\n#### Limit\nReturn a stream up to a limit.\n\nStops even if more data available if limit reached.\n\n```$stream-\u003elimit(int $limit): Stream```\n\n```php\nUse IterTools\\Single;\n\n$matrixMovies = ['The Matrix', 'The Matrix Reloaded', 'The Matrix Revolutions', 'The Matrix Resurrections'];\n$limit        = 1;\n\n$goodMovies = Stream::of($matrixMovies)\n    -\u003elimit($limit)\n    -\u003etoArray();\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```$stream-\u003emap(callable $function): Stream```\n\n```php\nuse IterTools\\Stream;\n\n$grades =","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkrogoyski%2Fitertools-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarkrogoyski%2Fitertools-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarkrogoyski%2Fitertools-php/lists"}