{"id":13828208,"url":"https://github.com/jeremeamia/iter8","last_synced_at":"2025-07-31T17:08:58.364Z","repository":{"id":56998022,"uuid":"192739305","full_name":"jeremeamia/iter8","owner":"jeremeamia","description":"PHP library for iterable/generator transformations and operations","archived":false,"fork":false,"pushed_at":"2019-07-19T23:08:28.000Z","size":174,"stargazers_count":49,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-24T01:17:24.559Z","etag":null,"topics":["functions","generators","iterable","iterators","php","transformations","yield"],"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/jeremeamia.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-06-19T13:36:48.000Z","updated_at":"2023-12-28T21:27:44.000Z","dependencies_parsed_at":"2022-08-21T14:50:20.899Z","dependency_job_id":null,"html_url":"https://github.com/jeremeamia/iter8","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/jeremeamia/iter8","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremeamia%2Fiter8","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremeamia%2Fiter8/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremeamia%2Fiter8/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremeamia%2Fiter8/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jeremeamia","download_url":"https://codeload.github.com/jeremeamia/iter8/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jeremeamia%2Fiter8/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268074932,"owners_count":24191636,"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","status":"online","status_checked_at":"2025-07-31T02:00:08.723Z","response_time":66,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["functions","generators","iterable","iterators","php","transformations","yield"],"created_at":"2024-08-04T09:02:36.676Z","updated_at":"2025-07-31T17:08:58.343Z","avatar_url":"https://github.com/jeremeamia.png","language":"PHP","readme":"# Iter8\n\n[![Made with Love](https://img.shields.io/badge/made_with-♥-ff69b4.svg)](https://github.com/jeremeamia/iter8/graphs/contributors)\n[![Coded in PHP](https://img.shields.io/badge/code-php7-8892bf.svg)](http://php.net/)\n[![Packagist Version](https://img.shields.io/packagist/v/jeremeamia/iter8.svg)](https://packagist.org/packages/jeremeamia/iter8)\n[![CircleCI](https://circleci.com/gh/jeremeamia/iter8/tree/master.svg?style=svg)](https://circleci.com/gh/jeremeamia/iter8/tree/master)\n\n## Introduction\n\nIter8 is a PHP library for iterable and functional operations (e.g., map, filter, reduce) implemented using generators.\n\nIter8 provides ways to create and transform any `iterable`s (e.g., generators, iterators, arrays, etc.) easily in order\nto deal with data sets that fit the `Iterator` pattern use case (i.e., typically data of large, paginated, infinite, or\nunknown length). Using iterators/generators generally provides benefits like lower memory consumption and lazy\nevaluation. Complex transformations can be defined via functional composition.\n\n## Usage\n\nIter8's core implementations reside as static methods in 3 classes:\n\n- `Iter` – Operations for iterable values. Some transform (e.g., map, filter) and some evaluate (e.g., reduce).\n- `Gen` – Factories for creating iterables from other values.\n- `Func` – Utilities for creating or transforming callables to be used with iterable operations.\n\nThere are 3 usage patterns for working with iterable values available in Iter8. Which you use is mostly a matter of\npreference:\n\n- `Iter` functions – Standard function-oriented usage with the `Iter` functions.\n- `Pipe` composition – Use of `Iter::pipe()` and the `Pipe::*` functions to compose a set of iterable transformations.\n- `Collection` object - An OOP interface to Iter8, that exposes the `Iter::*` and `Gen::*` functions as chainable\n  methods on an a collection-type object.\n  \nThe examples in the next section will demonstrate each of these usage patterns.\n\n## Examples\n\nGiven the following data set...\n\n```php\nconst PEOPLE = [\n    ['name' =\u003e 'Abby',  'age' =\u003e 19],\n    ['name' =\u003e 'Benny', 'age' =\u003e 21],\n    ['name' =\u003e 'Cally', 'age' =\u003e 22],\n    ['name' =\u003e 'Danny', 'age' =\u003e 24],\n    ['name' =\u003e 'Danny', 'age' =\u003e 24],\n    ['name' =\u003e 'Eddy',  'age' =\u003e 18],\n];\n```\n\n### Iter Functions\n\nWith this usage pattern, operations are applied procedurally and one-at-a-time.\n\n```php\n$iter = Gen::from(PEOPLE);\n$iter = Iter::filter($iter, Func::compose([\n    Func::index('age'),\n    Func::operator('\u003e=', 20),\n]));\n$iter = Iter::map($iter, Func::index('name'));\n$iter = Iter::debounce($iter);\n\nIter::print($iter);\n#\u003e ['Benny', 'Cally', 'Danny']\n```\n\n### Pipe Composition\n\nWith this usage pattern, operations are \"piped\" or composed together. The `Pipe` class delegates its operations back to\nthe `Iter` class, but manages the iterable value.\n\n```php\n$iter = Iter::pipe(Gen::from(PEOPLE), [\n    Pipe::filter(Func::compose([\n        Func::index('age'),\n        Func::operator('\u003e=', 20),\n    ])),\n    Pipe::map(Func::index('name')),\n    Pipe::debounce(),\n]);\n\nIter::print($iter);\n#\u003e ['Benny', 'Cally', 'Danny']\n```\n\nYou can \"switch\" the context of the iterable you are transforming in the middle of a pipe. This example evaluates the\nmax age from the iterable of people, and then switches to a new iterable using that max age value.\n\n```php\n$iter = Iter::pipe(Gen::from(PEOPLE), [\n    Pipe::pluck('age'),\n    Pipe::reduce('max'),\n    Pipe::switch(function (int $maxAge) {\n        return Gen::range(1, $maxAge);\n    }),\n]);\n\nIter::print($iter);\n#\u003e [1, 2, 3, ..., 22, 23, 24]\n```\n\n### Collection Object\n\nWith this usage pattern, the iterable is encapsulated as a `Collection` object. Calling methods on the collection object\ndelegate back to the `Iter` class, but the iterable is managed internally. Collections are immutable, so each\ntransformation returns a new instance. Also, unlike regular generators, collections can be rewound. Static method calls\non `Collection` are delegated to `Gen`, so the `Collection` object actually exposes the breadth of Iter8's functionality\nfrom one interface.\n\n```php\n$collection = Collection::from(PEOPLE)\n    -\u003efilter(Func::compose([\n        Func::index('age'),\n        Func::operator('\u003e=', 20),\n    ]))\n    -\u003emap(Func::index('name'))\n    -\u003edebounce();\n\n$collection-\u003eprint();\n#\u003e ['Benny', 'Cally', 'Danny']\n```\n\n## Rewindability\n\nGenerators are not rewindable (i.e., calling `rewind()` on them explicitly or trying `foreach` them again will cause an\nerror). Iter8 provides two ways to make generators/iterables rewindable.\n\n### Deferred Generators\n\nIf you are in control of the function that produces the generator (i.e., the one containing the `yield` statements),\nthen you can use the `Gen::defer()` function to wrap that generating function.\n\n```php\n$items = Gen::defer(function () use ($apiClient) {\n    $apiResult = $apiClient-\u003egetItems();\n    foreach ($apiResult['items'] as $data) {\n        yield Models\\Item::fromArray($data);\n    }\n});\n\n// ...\n// First iteration\nforeach ($items as $item) { /* ... */ }\n// ...\n// Another iteration\nforeach ($items as $item) { /* ... */ }\n```\n\n`Gen::defer()` returns a `DeferredGenerator` iterator, that defers producing the actual generator until the time of\niteration. If you rewind or iterate again, then the generating function is re-executed.\n\n### Rewindable Iterator\n\nIf you don't control the generating function or want to avoid re-executing the generator, then you can retroactively\nmake the iterable rewindable by using the `Iter::rewindable()` function.\n\n```php\n$apiResult = $apiClient-\u003egetItems();\n$items = Iter::map($apiResult['items'], function (array $data) {\n    return Models\\Item::fromArray($data);\n});\n$items = Iter::rewindable($items);\n\n// ...\n// First iteration\nforeach ($items as $item) { /* ... */ }\n// ...\n// Another iteration\nforeach ($items as $item) { /* ... */ }\n```\n\n`Iter::rewindable()` wraps the provided iterable in a `RewindableIterator`, which caches items during the first\niteration, such that they can be re-emitted in later iterations.\n\nAlso, `Collection`s are rewindable be default, since they extend the `RewindableIterator`.\n\n## Inspiration\n\nA lot of my recent work in PHP has dealt with iterators and generators, and fiddling about with large API result sets,\nso I wanted to put something like this together to share.\n\nHowever, some work like this has been done before in libraries like [nikic/iter][iter]. You'll notice that I have some\nsimilar functions and implementations. Some of my work here is inspired by that library, and some is straight borrowed.\n\nIn addition, I've taken some inspiration from the [ReactiveX][] project. Though generators and observables are not\nidentical concepts, they are similar, so some of the operations and names of operations have been borrowed when they\napply equally well to both generators and observables. Also, the concept of \"pipe\" from [RxJS][], the JavaScript\nimplementation of ReactiveX, is implemented in this project for composing transformations. In a way, this is also\nsimilar to how the [mtdowling/transducers.php][transducers] library works with its \"composable algorithmic\ntransformations\". The idea of \"transducers\" themselves are [borrowed from Clojure][clojure], so I've looked to several\nsources for ideas.\n\nFinally, I've also taken ideas from the [Laravel Collections][laravel] library, and though I also have some similar\nfunctions, my implementations vary greatly as they are founded upon generators, not arrays. This means that random\narray access of values in Iter8 collections is not supported.\n\n[iter]: https://github.com/nikic/iter\n[ReactiveX]: http://reactivex.io/\n[RxJS]: https://github.com/ReactiveX/rxjs\n[transducers]: https://github.com/mtdowling/transducers.php\n[clojure]: https://clojure.org/reference/transducers\n[laravel]: https://laravel.com/docs/5.8/collections\n","funding_links":[],"categories":["PHP"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeremeamia%2Fiter8","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjeremeamia%2Fiter8","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjeremeamia%2Fiter8/lists"}