Today I launched a new package: 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. 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!
$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.
$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
. 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!
Top comments (4)
Good job. Have you considered writing an RFC to add those methods in PHP core?
Very briefly, but I don't see this happening TBH. When I see way better features being written up as an RFC, and get shut down by the majority of the voters; I don't this will have a chance. But I'm very flattered you think this could be an RFC :-)
I am however still thinking about opening up an RFC for body-less constructors. This would encourage constructor property promotion, but in an interface like notation; without the useless empty body and curly braces. Soo much work (time) though.
I understand. Donβt hesitate to DM me if you need an upvote or something π
You could also propose those methods for the amazing Laravel Collection object through a PR, worst thing that can happen is to bring attention to your lib π