Skip to content

Instantly share code, notes, and snippets.

@mudge
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mudge/3cda558c73f7ea0f7a16 to your computer and use it in GitHub Desktop.
Save mudge/3cda558c73f7ea0f7a16 to your computer and use it in GitHub Desktop.
Fun with PHP's Iterator classes to implement a lazy flat map.
<?php
/* From https://github.com/guzzle/iterator/blob/master/MapIterator.php */
class MapIterator extends \IteratorIterator
{
/** @var mixed Callback */
protected $callback;
/**
* @param \Traversable $iterator Traversable iterator
* @param array|\Closure $callback Callback used for iterating
*
* @throws InvalidArgumentException if the callback if not callable
*/
public function __construct(\Traversable $iterator, $callback)
{
parent::__construct($iterator);
if (!is_callable($callback)) {
throw new InvalidArgumentException('The callback must be callable');
}
$this->callback = $callback;
}
public function current()
{
return call_user_func($this->callback, parent::current());
}
}
class FlatMapIterator extends MapIterator implements RecursiveIterator
{
/**
* As we will return a RecursiveIterator from our callback, simply return
* the current value.
*/
public function getChildren()
{
return $this->current();
}
/**
* As above, this will always be true.
*/
public function hasChildren()
{
return true;
}
}
$fmi = new FlatMapIterator(new ArrayIterator(array(1, 2, 3)), function ($x) {
/* In order to use RecursiveIteratorIterator, this must be an implementation of
* RecursiveIterator.
*/
return new RecursiveArrayIterator(array($x, $x + 1, $x + 2));
});
$ri = new RecursiveIteratorIterator($fmi, RecursiveIteratorIterator::LEAVES_ONLY);
/**
* Prints:
* int(1)
* int(2)
* int(3)
* int(2)
* int(3)
* int(4)
* int(3)
* int(4)
* int(5)
*/
foreach ($ri as $i) {
var_dump($i);
}
/**
* You can happily nest FlatMapIterators too...
*/
$fmi2 = new FlatMapIterator(new ArrayIterator(array(1, 2, 3)), function ($x) {
return new FlatMapIterator(new ArrayIterator(array('a', 'b', 'c')), function ($y) use ($x) {
return new RecursiveArrayIterator(array($x, $y));
});
});
$ri2 = new RecursiveIteratorIterator($fmi2, RecursiveIteratorIterator::LEAVES_ONLY);
/**
* Prints:
* int(1)
* string(1) "a"
* int(1)
* string(1) "b"
* int(1)
* string(1) "c"
* int(2)
* string(1) "a"
* int(2)
* string(1) "b"
* int(2)
* string(1) "c"
* int(3)
* string(1) "a"
* int(3)
* string(1) "b"
* int(3)
* string(1) "c"
*/
foreach ($ri2 as $i) {
var_dump($i);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment