Iteration primitives using generators

Last update: May 24, 2022

Iteration primitives using generators

This library implements iteration primitives like map() and filter() using generators. To a large part this serves as a repository for small examples of generator usage, but of course the functions are also practically quite useful.

All functions in this library accept arbitrary iterables, i.e. arrays, traversables, iterators and aggregates, which makes it quite different from functions like array_map() (which only accept arrays) and the SPL iterators (which usually only accept iterators, not even aggregates). The operations are of course lazy.

Install

To install with composer:

composer require nikic/iter

Functionality

A small usage example for the map() and range() functions:

<?php

use iter\func;

require 'path/to/vendor/autoload.php';

$nums = iter\range(1, 10);
$numsTimesTen = iter\map(func\operator('*', 10), $nums);
// => iter(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)

You can find documentation and usage examples for the individual functions in iter.php, here I only list the function signatures as an overview:

Iterator map(callable $function, iterable $iterable)
Iterator mapWithKeys(callable $function, iterable $iterable)
Iterator mapKeys(callable $function, iterable $iterable)
Iterator flatMap(callable $function, iterable $iterable)
Iterator reindex(callable $function, iterable $iterable)
Iterator filter(callable $predicate, iterable $iterable)
Iterator enumerate(iterable $iterable)
Iterator toPairs(iterable $iterable)
Iterator fromPairs(iterable $iterable)
Iterator reductions(callable $function, iterable $iterable, mixed $startValue = null)
Iterator zip(iterable... $iterables)
Iterator zipKeyValue(iterable $keys, iterable $values)
Iterator chain(iterable... $iterables)
Iterator product(iterable... $iterables)
Iterator slice(iterable $iterable, int $start, int $length = INF)
Iterator take(int $num, iterable $iterable)
Iterator drop(int $num, iterable $iterable)
Iterator takeWhile(callable $predicate, iterable $iterable)
Iterator dropWhile(callable $predicate, iterable $iterable)
Iterator keys(iterable $iterable)
Iterator values(iterable $iterable)
Iterator flatten(iterable $iterable, int $levels = INF)
Iterator flip(iterable $iterable)
Iterator chunk(iterable $iterable, int $size, bool $preserveKeys = false)
Iterator chunkWithKeys(iterable $iterable, int $size)
Iterator toIter(iterable $iterable)

Iterator range(number $start, number $end, number $step = null)
Iterator repeat(mixed $value, int $num = INF)
Iterator split(string $separator, string $data)

mixed    reduce(callable $function, iterable $iterable, mixed $startValue = null)
bool     any(callable $predicate, iterable $iterable)
bool     all(callable $predicate, iterable $iterable)
mixed    search(callable $predicate, iterable $iterable)
void     apply(callable $function, iterable $iterable)
string   join(string $separator, iterable $iterable)
int      count(iterable $iterable)
bool     isEmpty(iterable $iterable)
mixed    recurse(callable $function, $iterable)
array    toArray(iterable $iterable)
array    toArrayWithKeys(iterable $iterable)
bool     isIterable($value)

As the functionality is implemented using generators the resulting iterators are by default not rewindable. This library implements additional functionality to allow creating rewindable generators.

You can find documentation for this in iter.rewindable.php, here is just a small usage example of the two main functions:

<?php

use iter\func;

require 'path/to/vendor/autoload.php';

/* Create a rewindable map function which can be used multiple times */
$rewindableMap = iter\makeRewindable('iter\\map');
$res = $rewindableMap(func\operator('*', 3), [1, 2, 3]);

/* Do a rewindable call to map, just once */
$res = iter\callRewindable('iter\\map', func\operator('*', 3), [1, 2, 3]);

The above functions are only useful for your own generators though, for the iter generators rewindable variants are directly provided with an iter\rewindable prefix:

$res = iter\rewindable\map(func\operator('*', 3), [1, 2, 3]);
// etc

GitHub

https://github.com/nikic/iter
Comments
  • 1. Add failing test for slice() iterating too much

    I was expecting slice() to not iterate further if its limit value is 1 and it already found a value.

    I have added a failing test for if this really is undesired behavior.

    Reviewed by amcsi at 2016-08-02 08:47
  • 2. Version 2.0

    I'd like to release version 2.0 with PHP version requirement bumped to 7.1 and added type hints.

    Are there any other (breaking) changes that should be done now?

    Reviewed by nikic at 2018-01-02 20:12
  • 3. Associate

    • Added the associate function to inverse enumerate

    A good example of where this would be useful is if you need to map over the keys and values at the same time:

    $dict = [
        'a' => 1,
        'b' => 2,
        'c' => 3,
    ];
    iter\associate(iter\map(function($tup) {
          return [strtoupper($tup[0], $tup[1] * 2];
    }, iter\enumerate($dict));
    // iter('A' => 2, 'B' => 4, 'C' => 6)
    

    Basically, it provides a much cleaner way to un-enumerate an iterable.

    Reviewed by ragboyjr at 2017-12-29 08:10
  • 4. Product combinator generator function

    Implementation of a product generator function, that returns the cartesian product of 0 or more iterators. The key is a tuple of the iteratables keys, and the value a tuple of the iterables values.

    Example:

    // Returns [[1, 3], [1, 4], [1, 5], [2, 3], [2, 4], [2, 5]]
    toArray(product([1, 2], [3, 4, 5]));
    
    Reviewed by nicmart at 2015-05-28 21:42
  • 5. Pass key to map function

    Is there any reason for not passing both key and value to the mapKeys and map functions?

    I find myself often wanting to use both even when just changing one.

    Currently one option is to use enumerate, but it makes the code a lot longer than it needs to be.

    Reviewed by SamMousa at 2018-03-28 11:24
  • 6. Chunk function to return an Iterator of Iterable chunks

    Takes an iterable and chunks it into the specified size.

    This function is lazy in that it yields each chunk as a generator which when iterated will yield size times or until the iterator is no longer valid

    Examples:

      iter\chunk([1, 2, 3], 2)
      => iter(iter(1, 2), iter(3))
    
    Reviewed by camspiers at 2014-01-27 00:39
  • 7. Added iter\reindex and iter\fn\path

    These two solve a use case which i often have.

    You have a nested associative array like this:

    $securities = [
        [
            'seccode' => 'HT-R-A',
            'isin' => 'HRHT00RA0005',
            'issuer' => [
                'code' => 'HT',
                'name' => 'Hrvatski Telekom d.d.'
            ],
        ],
        [
            'seccode' => 'INGR-O-11CA',
            'isin' => 'HRINGRO11CA1',
            'issuer' => [
                'code' => 'INGR',
                'name' => 'Ingra d.d.'
            ],
        ]
    ];
    

    And you want to iterate over it, but with the key being a value from the array.

    $path = iter\fn\path('issuer', 'code');
    $iter = iter\reindex($securities, $path);
    foreach(iter\keys($iter) as $key) {
        var_dump($key);
    }
    

    This yields:

    string(2) "HT"
    string(4) "INGR"
    
    Reviewed by ihabunek at 2014-01-13 10:00
  • 8. PHP 8.1: fix deprecation notices

    A run on PHP 8.1 currently shows:

    PHP Deprecated:  Return type of iter\rewindable\_RewindableGenerator::current() should either be compatible with Iterator::current(): mixed, or the #[ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/runner/work/iter/iter/src/iter.rewindable.php on line 123
    PHP Deprecated:  Return type of iter\rewindable\_RewindableGenerator::next() should either be compatible with Iterator::next(): void, or the #[ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/runner/work/iter/iter/src/iter.rewindable.php on line 108
    PHP Deprecated:  Return type of iter\rewindable\_RewindableGenerator::key() should either be compatible with Iterator::key(): mixed, or the #[ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/runner/work/iter/iter/src/iter.rewindable.php on line 118
    PHP Deprecated:  Return type of iter\rewindable\_RewindableGenerator::valid() should either be compatible with Iterator::valid(): bool, or the #[ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/runner/work/iter/iter/src/iter.rewindable.php on line 113
    PHP Deprecated:  Return type of iter\rewindable\_RewindableGenerator::rewind() should either be compatible with Iterator::rewind(): void, or the #[ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/runner/work/iter/iter/src/iter.rewindable.php on line 103
    PHP Deprecated:  Return type of iter\_CountableTestDummy::count() should either be compatible with Countable::count(): int, or the #[ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /home/runner/work/iter/iter/test/iterTest.php on line 625
    

    These deprecation notices relate to the Return types for internal methods RFC in PHP 8.1.

    Using the attribute will silence the deprecation notices for now for those methods for which it cannot be added yet (PHP 8.0 mixed). For all other methods, the return type has now been added.

    Reviewed by jrfnl at 2021-08-02 14:07
  • 9. Add mapWithKeys()

    #64 caught my eye. I noticed that changing how the existing methods worked would cause issues, so I made a new mapWithKeys() method that passes the iterator key in addition to its value to the callback.

    Here is a copy/paste of the test I wrote for it, to see it in action:

    $mapped = mapWithKeys(func\operator('*'), range(0, 5));
    $this->assertSame([0, 1, 4, 9, 16, 25], toArray($mapped));
    
    $mapped = mapWithKeys(
        function ($v, $k) { return sprintf('%s%s', $k, $v); },
        ['foo' => 'bar', 'bing' => 'baz']
    );
    $this->assertSame(
        ['foo' => 'foobar', 'bing' => 'bingbaz'],
        toArrayWithKeys($mapped)
    );
    
    Reviewed by judahnator at 2019-08-23 15:50
  • 10. Feature request for iter\concat

    Similar to the iter\flatten, except it flattens by 1 step. This is very useful in many areas where your generators are yielding arrays and you want that to be unit of data to work with.

    Here's an implementation:

    function concat ($iterable) {
        return iter\reduce(
            function ($chainedIterables, $nextIterable) {
                return iter\chain($chainedIterables, $nextIterable);
            },
            $iterable,
            []
        );
    }
    

    This should be equivalent to a foldl concat implementation in Haskell.

    Reviewed by CMCDragonkai at 2017-03-24 06:39
  • 11. Array keys in callable for 'reduce' function

    I think will be better if developers will be able to get a key.

    Example:

    use iter as f;
    
    f\reduce(function ($acc, $value, $key) {
        $acc .= sprintf("%s: %s\n", $key, $value);
        return $acc;
    }, $response->headers->all(), '');
    

    Or I can do it otherwise?

    Reviewed by alxsad at 2015-08-21 12:59
  • 12. IteratorAggregate not properly handled in toIter

    The return type of IteratorAggregate::getIterator is Traversable, but toIter assumes the value returned will be of type Iterator. The return type check on toIter therefore fails when getIterator returns another IteratorAggregate.

    Reviewed by jbcollings at 2022-03-17 23:09
  • 13. Add a json_encode

    I don't know if it would be relevant or not. Some benchmark might be required to determine whether or not the original json_encode would fail on big arrays with big objects. If so using iterators could become useful. Comparing to the original one there would be the need for special const such as FORCE_OBJECT(which already exists) and a FORCE_LIST

    Reviewed by Neirda24 at 2021-06-13 20:46
  • 14. Function proposal - at/get()

    We have a use case where we need to get a value by its index.

    In that sense, I'd like to suggest introducing a function at/get(iterable $iterable, int $index) : ?int that takes an iterable and returns the item at the specified index.

    I'll be glad to provide a PR if welcome.

    Reviewed by marcospassos at 2018-08-12 19:11
  • 15. Add head function

    Would be nice to have a function that fetches the head of an iterable and throws an exception if it's non-empty instead of having to call the current function on an iterator and checking whether it's not null.

    Reviewed by jankramer at 2018-08-09 06:39
Related tags
This is a port of the original WireGuard UI bits as implemented by Netgate in pfSense 2.5.0 to a package suitable for rapid iteration and more frequent updating on future releases of pfSense.

This is a port of the original WireGuard*** UI bits as implemented by Netgate in pfSense 2.5.0 to a package suitable for sideloading and more frequent updating on future releases of pfSense. This also includes some improvments such as a proper status page (found under Status / WireGuard Status) and improved assigned interface handling.

May 13, 2022
Primitives for functional programming in PHP
Primitives for functional programming in PHP

Functional PHP: Functional primitives for PHP NOTE: functional-php used to come with a C extension that implemented most of the functions natively. As

May 24, 2022
Dictionary of attack patterns and primitives for black-box application fault injection and resource discovery.

FuzzDB was created to increase the likelihood of finding application security vulnerabilities through dynamic application security testing. It's the f

May 20, 2022
🐼 Framework agnostic package using asynchronous HTTP requests and PHP generators to load paginated items of JSON APIs into Laravel lazy collections.

Framework agnostic package using asynchronous HTTP requests and generators to load paginated items of JSON APIs into Laravel lazy collections.

Apr 30, 2022
This package extends the core file generators that are included with Laravel 5
This package extends the core file generators that are included with Laravel 5

Extended Migration Generators for Laravel 6, 7 and 8 Easily define the migration schema right in your make:migration command. The new commands this pa

May 17, 2022
A collection of generators for Lumen and Laravel 5.

Lumen generators A collection of generators for Lumen and Laravel 5. Contents Why ? Installation Quick Usage Detailed Usage Model Generator Migration

Mar 24, 2022
Generate form validators for Laravel: an extension of way/generators

Laravel Form Validator Contents Introduction Installation Usage Example Tests Introduction After using Jeffrey Way's Generator and his Validator packa

Jan 14, 2022
Rapidly speed up your Laravel workflow with generators

Fast Workflow in Laravel With Custom Generators This Laravel package provides a variety of generators to speed up your development process. These gene

May 12, 2022
Laravel CRUD Generator This Generator package provides various generators like CRUD, API, Controller, Model, Migration, View for your painless development of your applications.
Laravel CRUD Generator This Generator package provides various generators like CRUD, API, Controller, Model, Migration, View for your painless development of your applications.

Laravel CRUD Generator This Generator package provides various generators like CRUD, API, Controller, Model, Migration, View for your painless develop

May 8, 2022
Laracademy Generators - is a tool set that helps speed up the development process of a Laravel application.

Laracademy Generators Laracademy Generators - is a tool set that helps speed up the development process of a Laravel application. Author(s): Laracadem

May 14, 2022
Laravel File Generators with config and publishable stubs

Laravel File Generators Custom Laravel File Generators with a config file and publishable stubs. You can publish the stubs. You can add your own stubs

May 5, 2022
A module allowing you to write your Processwire template using MJML and get a converted HTML output using MJML API.
A module allowing you to write your Processwire template using MJML and get a converted HTML output using MJML API.

PageMjmlToHtml A module allowing you to write your Processwire template using MJML and get a converted HTML output using MJML API. This is considered

May 2, 2022
This repository demonstrates exemplary implementation of chat using HTTP and Websocket servers in PHP using Kraken Framework components.
This repository demonstrates exemplary implementation of chat using HTTP and Websocket servers in PHP using Kraken Framework components.

This repository demonstrates exemplary implementation of chat using HTTP and Websocket servers in PHP using Kraken Framework components.

Aug 11, 2021
A research raw data repository for researchers of Arba Minch University built using Codeigniter which follows MVC architecture. The front-end is build using Bootstrap.

Arba Minch University Dataset Repository This system is a research dataset repository for Arba Minch University researchers and is build using Codeign

Apr 7, 2022
This PHP library will help you to work with your Pinterest account without using any API account credentials.
This PHP library will help you to work with your Pinterest account without using any API account credentials.

Pinterest Bot for PHP A PHP library to help you work with your Pinterest account without API credentials. The Pinterest API is painful: receiving an a

May 22, 2022
Cache slam defense using a semaphore to prevent dogpile effect.

metaphore PHP cache slam defense using a semaphore to prevent dogpile effect (aka clobbering updates, stampending herd or Slashdot effect). Problem: t

Apr 8, 2022
A static php code analysis tool using the Graph Theory
A static php code analysis tool using the Graph Theory

Mondrian Ok guyz, you have a master degree in Graph Theory, you follow Law of Demeter and you live on S.O.L.I.D principles ? Let's have some Fun ! (^ω

May 22, 2022
Lovely PHP wrapper for using the command-line

ShellWrap What is it? It's a beautiful way to use powerful Linux/Unix tools in PHP. Easily and logically pipe commands together, capture errors as PHP

May 3, 2022
[READ-ONLY] A flexible, lightweight and powerful Object-Relational Mapper for PHP, implemented using the DataMapper pattern. This repo is a split of the main code that can be found in https://github.com/cakephp/cakephp

CakePHP ORM The CakePHP ORM provides a powerful and flexible way to work with relational databases. Using a datamapper pattern the ORM allows you to m

Feb 24, 2022