Introducing Iterator Functions package
Tags: performance, webdev, php, package
Published: 2021-08-13 09:14:14

Today I launched a new package: [Iterator Functions](https://github.com/doekenorg/iterator-functions). It's a package that helps with everyday usage of Iterators & Generators.

I recently posted a blog on why I think you should [use Generators instead of arrays](/blog/generators-over-arrays). In this post I mentioned the caveat that Generators, and by extension any `Traversable`, could not be used in conjunction with `array_` functions. This is kind of a bummer, because those functions are very handy. And it got me thinking: why shouldn't these functions exist? So here they are.

## How to use Iterator Functions

The package provides 8 functions to help with `Iterators`. Let's look at two examples. First off, `iterator_map()`.

I use `array_map()` all the time. It's a handy way of quickly changing all values of an array into something else, by applying a callback function on it. The downside of this is memory consumption. You transform the entire array at once. Only after that can you use, or traverse, every new item in the array.

When using `iterator_map()` you get the best of both worlds. The ease of use of an `array_`-like function, and the added benefit of less memory consumption. This is because the callback is applied while traversing the iterator. Meaning only that value is in memory at that time. As an added bonus: `iterator_map()` allows for `iterable` types to be passed. That means an `array` is also allowed. So even with the use of an array you get the memory preserving benefits!

```php
$ids_iterator = new ArrayIterator([1, 2, 3]);
$ids_array = [4, 5, 6];

$mapped_iterator = iterator_map(function(int $id): Model {
    return Model::find($id);
}, $ids_iterator, $ids_array);

foreach ($mapped_iterator as $model) {
    // Here the callback is applied, and the model is returned.
}
```

In this example we iterate over the values 1 through 6, and retrieve the corresponding `Model` instance.

Another example is `iterator_reduce()`. You can use the `array_reduce()` function to flatten an array into a single new value by applying a callback on every iteration. This callback also receives the previous value. The last iteration effectively decides what the final output will be. `iterator_reduce()` does the same thing, but with an `Iterator`.

As an added bonus, and unlike `array_reduce()`, the callback of `iterator_reduce()` also receives the **key** of the current iteration.

```php
$iterator = new ArrayIterator(['name' => 'Doeke Norg', 'age' => 33]);

$output = iterator_reduce($iterator, function($carry, $value, string $key): string {
    return str_replace('%' . $key . '%', $value, $carry);
}, 'My name is: %name%. I am %age% years old.');

// $output = My name is: Doeke Norg. I am 33 years old.
```

This is a rather silly example, but it's only to get the point across. We change the initial value, which is provided by the 3rd parameter, and replace the key of the current iteration with the value. So the string is actually changed twice; first to replace the name, and second to replace the age.

## Try it out

I'd love for you to try out [`doekenorg/iterator-functions`](https://github.com/doekenorg/iterator-functions). Besides the obvious helper functions, I added a few extra `Iterator` types that can be useful. Most of the functions actually use these iterators to preform their task.

If you have any feedback please let me know!