composer require gielfeldt/iterators
This library contains a bunch of various iterators that I made primarily for getting more acquainted with iterators in PHP. Some may be useful. Some may be silly.
Enjoy!
Cache an iterator for multiple iterations.
use Gielfeldt\Iterators\CachingIterator;
$input = new \ArrayIterator(range(1, 4));
$input = new \NoRewindIterator($input);
$cached = new CachingIterator($input);
print_r(iterator_to_array($cached));
print_r(iterator_to_array($cached));
Output:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
Generate a checksum for an iterator, either per iteration or the entire dataset.
use Gielfeldt\Iterators\ChecksumIterator;
$input = new \ArrayIterator([
['key1' => 'value1'],
['key2' => 'value2'],
['key3' => 'value3'],
]);
$iterator = new ChecksumIterator($input, 'md5');
foreach ($iterator as $key => $value) {
print "$key => $value\n";
}
var_dump($iterator->getChecksum());
var_dump((string) $iterator);
Output:
0 => e2e517365ffe6fedd279364e3fa74786
1 => f0a0db0fc9abe193b21fd657fe678884
2 => c04f606bb5bba82282dfa93edb59c6ee
string(32) "4fd19adc845da6fdd9c7c394f4626bac"
string(32) "4fd19adc845da6fdd9c7c394f4626bac"
Split an iterator into chunks of iterators.
use Gielfeldt\Iterators\ChunkIterator;
use Gielfeldt\Iterators\AtomicTempFileObject;
// Split a file into multiple files of a 100 lines each.
$file = new \SplFileObject('inputfile');
foreach (new ChunkIterator($file, 100) as $i => $lines) {
AtomicTempFileObject::file_put_contents("outputfile.part.$i", implode("", iterator_to_array($lines)));
}
Clone each value in iteration.
use Gielfeldt\Iterators\CloningIterator;
$object1 = (object) ['value' => 'test1'];
$object2 = (object) ['value' => 'test2'];
$object3 = (object) ['value' => 'test3'];
$input = new \ArrayIterator([$object1, $object2, $object3]);
$iterator = new CloningIterator($input);
$cloned = iterator_to_array($iterator);
$object1->value = 'MODIFIED';
var_dump(iterator_to_array($input));
var_dump($cloned);
Output:
array(3) {
[0] =>
class stdClass#2 (1) {
public $value =>
string(8) "MODIFIED"
}
[1] =>
class stdClass#3 (1) {
public $value =>
string(5) "test2"
}
[2] =>
class stdClass#4 (1) {
public $value =>
string(5) "test3"
}
}
array(3) {
[0] =>
class stdClass#9 (1) {
public $value =>
string(5) "test1"
}
[1] =>
class stdClass#10 (1) {
public $value =>
string(5) "test2"
}
[2] =>
class stdClass#11 (1) {
public $value =>
string(5) "test3"
}
}
Similar to array_combine(). However, iterators can have non-unique keys. Be aware of this when using iterator_to_array();
use Gielfeldt\Iterators\CombineIterator;
$keys = new \ArrayIterator(['key1', 'key2', 'key3', 'key1', 'key2', 'key3']);
$values = new \ArrayIterator(['value1', 'value2', 'value3', 'value4', 'value5', 'value6']);
$iterator = new CombineIterator($keys, $values);
foreach ($iterator as $key => $value) {
print "$key => $value\n";
}
print_r(iterator_to_array($iterator));
Output:
key1 => value1
key2 => value2
key3 => value3
key1 => value4
key2 => value5
key3 => value6
Array
(
[key1] => value4
[key2] => value5
[key3] => value6
)
Takes any iterator and makes it countable, simply by iterating through it and counting.
use Gielfeldt\Iterators\CountableIterator;
$some_noncountable_iterator = new \IteratorIterator(new \ArrayIterator([1, 2, 3]));
$iterator = new CountableIterator($some_noncountable_iterator);
var_dump(count($iterator));
Output:
int(3)
Compares two iterators. Similar to array_diff(). Possible to set a custom compare function.
use Gielfeldt\Iterators\DiffIterator;
$input1 = new \ArrayIterator(['key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3']);
$input2 = new \ArrayIterator(['key11' => 'value1', 'key22' => 'value1', 'key2' => 'value3']);
$input3 = new \ArrayIterator(['key1' => 'value2', 'key2' => 'value2', 'key33' => 'value3']);
$iterator = new DiffIterator($input1, $input2, $input3);
$iterator->setDiff(function ($iterator, $key, $value) {
return $iterator->key() == $key && $iterator->current() == $value;
});
print_r(iterator_to_array($iterator));
Output:
Array
(
[key1] => value1
[key3] => value3
)
Provide an end condition through an anonymous function for an iterator.
use Gielfeldt\Iterators\FiniteIterator;
$input = new \ArrayIterator(range(1, 8));
$some = new FiniteIterator($input, function ($iterator) {
return $iterator->current() > 5;
});
print_r(iterator_to_array($some));
Output:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
[4] => 5
)
Similar to array_flip(). However, iterators can have non-unique keys. Be aware of this when using iterator_to_array();
use Gielfeldt\Iterators\FlipIterator;
$input = new \ArrayIterator([
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
'key4' => 'value1',
'key5' => 'value2',
'key6' => 'value3',
]);
$iterator = new FlipIterator($input);
foreach ($iterator as $key => $value) {
print "$key => $value\n";
}
print_r(iterator_to_array($iterator));
Output:
value1 => key1
value2 => key2
value3 => key3
value1 => key4
value2 => key5
value3 => key6
Array
(
[value1] => key4
[value2] => key5
[value3] => key6
)
Similar to \GlobIterator, but supports **
use Gielfeldt\Iterators\GlobIterator;
$iterator = new GlobIterator('/tmp/**.log');
var_dump(iterator_to_array($iterator));
Output:
array(2) {
'/tmp/one.log' =>
class Gielfeldt\Iterators\GlobIteratorFileInfo#17 (2) {
private $pathName =>
string(20) "/tmp/one.log"
private $fileName =>
string(10) "one.log"
}
'/tmp/somedir/two.log' =>
class Gielfeldt\Iterators\GlobIteratorFileInfo#16 (2) {
private $pathName =>
string(20) "/tmp/somedir/two.log"
private $fileName =>
string(15) "two.log"
}
}
Iterate only through certain rows of an iterator.
use Gielfeldt\Iterators\IndexIterator;
$input = new \ArrayIterator([
'test1' => 'val6',
'test2' => 'val5',
'test3' => 'val4',
'test4' => 'val3',
'test5' => 'val2',
'test6' => 'val1',
]);
$some = new IndexIterator($input, [2, 3, 5]);
print_r(iterator_to_array($some));
Output:
Array
(
[test3] => val4
[test4] => val3
[test6] => val1
)
Like SPL's InfiniteIterator but provides an additional method, getCurrentIteration().
use Gielfeldt\Iterators\InfiniteIterator;
use Gielfeldt\Iterators\FiniteIterator;
$input = new \ArrayIterator(range(1, 4));
$infinite = new InfiniteIterator($input);
$some = new FiniteIterator($infinite, function ($iterator) {
return $iterator->getCurrentIteration() >= 2 && $iterator->current() > 2;
});
foreach ($some as $k => $v) {
print $some->getCurrentIteration() . ": $k => $v\n";
}
Output:
0: 0 => 1
0: 1 => 2
0: 2 => 3
0: 3 => 4
1: 0 => 1
1: 1 => 2
1: 2 => 3
1: 3 => 4
2: 0 => 1
2: 1 => 2
Interleave multiple iterators.
use Gielfeldt\Iterators\InterleaveIterator;
$input1 = new \ArrayIterator([
'key1' => 'value11',
'key2' => 'value12',
'key3' => 'value13',
]);
$input2 = new \ArrayIterator([
'key21' => 'value21',
'key22' => 'value22',
'key23' => 'value23',
]);
$input3 = new \ArrayIterator([
'key1' => 'value31',
'key2' => 'value32',
'key3' => 'value33',
]);
$iterator = new InterleaveIterator($input1, $input2, $input3);
foreach ($iterator as $key => $value) {
print "$key => $value\n";
}
print_r(iterator_to_array($iterator));
Output:
key1 => value11
key21 => value21
key1 => value31
key2 => value12
key22 => value22
key2 => value32
key3 => value13
key23 => value23
key3 => value33
Array
(
[key1] => value31
[key21] => value21
[key2] => value32
[key22] => value22
[key3] => value33
[key23] => value23
)
Similar to array_intersect(). Possible to set a custom compare function.
use Gielfeldt\Iterators\IntersectIterator;
$input1 = new \ArrayIterator(['key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3']);
$input2 = new \ArrayIterator(['key11' => 'value1', 'key1' => 'value1', 'key2' => 'value3']);
$input3 = new \ArrayIterator(['key1' => 'value2', 'key2' => 'value2', 'key1' => 'value1']);
$iterator = new IntersectIterator($input1, $input2, $input3);
$iterator->setDiff(function ($iterator, $key, $value) {
return $iterator->key() == $key && $iterator->current() == $value;
});
print_r(iterator_to_array($iterator));
Output:
Array
(
[key1] => value1
)
Like SPL's IteratorIterator, but with a more meaningful name :-) and provides a getIndex() method.
use Gielfeldt\Iterators\TraversableIterator;
$keys = range(1, 10);
shuffle($keys);
$values = range(1, 10);
shuffle($values);
$input = new \ArrayIterator(array_combine($keys, $values));
$iterator = new TraversableIterator($input);
foreach ($iterator as $k => $v) {
print $iterator->getIndex() . ": $k => $v\n";
}
Output:
0: 1 => 3
1: 4 => 2
2: 9 => 10
3: 3 => 8
4: 2 => 6
5: 5 => 5
6: 8 => 1
7: 10 => 9
8: 6 => 7
9: 7 => 4
Similar to array_keys().
use Gielfeldt\Iterators\KeysIterator;
$input = new \ArrayIterator([
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
]);
$iterator = new KeysIterator($input);
print_r(iterator_to_array($iterator));
Output:
Array
(
[0] => key1
[1] => key2
[2] => key3
)
Similar to array_map().
use Gielfeldt\Iterators\MapIterator;
$input = new \ArrayIterator([
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
'key4' => 'value1',
'key5' => 'value2',
'key6' => 'value3',
]);
// Flip keys and values.
$iterator = new MapIterator($input, function ($iterator) {
return [$iterator->current(), $iterator->key()];
});
foreach ($iterator as $key => $value) {
print "$key => $value\n";
}
print_r(iterator_to_array($iterator));
Output:
value1 => key1
value2 => key2
value3 => key3
value1 => key4
value2 => key5
value3 => key6
Array
(
[value1] => key4
[value2] => key5
[value3] => key6
)
Selects a set of random elements from a traversable. Does not work with iterators that can only be traversed once. For that, see the ReservoirSamplingIterator.
use Gielfeldt\Iterators\RandomIterator;
$input = new \ArrayIterator(range(1, 10));
$random = new RandomIterator($input, 4);
var_dump(count($random));
print_r(iterator_to_array($random));
Output:
int(4)
Array
(
[1] => 2
[6] => 7
[7] => 8
[8] => 9
)
Repeat and iterator n times.
use Gielfeldt\Iterators\RepeatIterator;
$input = new \ArrayIterator([
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
]);
$iterator = new RepeatIterator($input, 3);
foreach ($iterator as $key => $value) {
print "$key => $value\n";
}
print_r(iterator_to_array($iterator));
Output:
key1 => value1
key2 => value2
key3 => value3
key1 => value1
key2 => value2
key3 => value3
key1 => value1
key2 => value2
key3 => value3
Array
(
[key1] => value1
[key2] => value2
[key3] => value3
)
Just like TraversableIterator but with a setInnerIterator() method.
use Gielfeldt\Iterators\ReplaceableIterator;
$iterator = new ReplaceableIterator(new \ArrayIterator(range(1, 4)));
print_r(iterator_to_array($iterator));
$iterator->setInnerIterator(new \ArrayIterator(range(5, 8)));
print_r(iterator_to_array($iterator));
Output:
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 4
)
Array
(
[0] => 5
[1] => 6
[2] => 7
[3] => 8
)
Like RandomIterator but works more effeciently on iterators of unknown size or which can be traversed only once.
use Gielfeldt\Iterators\ReservoirSamplingIterator;
$input = new \ArrayIterator(range(1, 10));
$input = new \NoRewindIterator($input);
$random = new ReservoirSamplingIterator($input, 4);
print_r(iterator_to_array($random));
Output:
int(4)
Array
(
[1] => 2
[6] => 7
[7] => 8
[8] => 9
)
Iterate randomly over a traversable.
use Gielfeldt\Iterators\ShuffleIterator;
$input = new \ArrayIterator(range(1, 10));
$shuffled = new ShuffleIterator($input);
print_r(iterator_to_array($shuffled));
print_r(iterator_to_array($shuffled));
Output:
Array
(
[1] => 2
[4] => 5
[0] => 1
[9] => 10
[7] => 8
[5] => 6
[2] => 3
[8] => 9
[3] => 4
[6] => 7
)
Array
(
[1] => 2
[9] => 10
[4] => 5
[2] => 3
[5] => 6
[0] => 1
[8] => 9
[3] => 4
[7] => 8
[6] => 7
)
use Gielfeldt\Iterators\SortIterator;
$input = new \ArrayIterator([6, 3, 2, 7, 1, 9]);
$iterator = new SortIterator($input);
print_r(iterator_to_array($iterator));
$input = new \ArrayIterator([6, 3, 2, 7, 1, 9]);
$iterator = new SortIterator($input, SortIterator::SORT_DESC);
print_r(iterator_to_array($iterator));
$input = new \ArrayIterator([6, 3, 2, 7, 1, 9]);
$iterator = new SortIterator($input, SortIterator::SORT_ASC, SortIterator::SORT_REINDEX);
print_r(iterator_to_array($iterator));
$input = new \ArrayIterator([6, 3, 2, 7, 1, 9]);
$iterator = new SortIterator($input, SortIterator::SORT_ASC, SortIterator::SORT_REINDEX, function ($a, $b) {
return -$a->current <=> -$b->current;
});
print_r(iterator_to_array($iterator));
Output:
Array
(
[4] => 1
[2] => 2
[1] => 3
[0] => 6
[3] => 7
[5] => 9
)
Array
(
[5] => 9
[3] => 7
[0] => 6
[1] => 3
[2] => 2
[4] => 1
)
Array
(
[0] => 1
[1] => 2
[2] => 3
[3] => 6
[4] => 7
[5] => 9
)
Array
(
[0] => 9
[1] => 7
[2] => 6
[3] => 3
[4] => 2
[5] => 1
)
Iterate over a traversable in steps.
use Gielfeldt\Iterators\StepIterator;
$input = new \ArrayIterator(range(1, 10));
$stepped = new StepIterator($input, 2);
print_r(iterator_to_array($stepped));
Output:
Array
(
[0] => 1
[2] => 3
[4] => 5
[6] => 7
[8] => 9
)
Similar to array_unique(). Also supports a custom callback function.
use Gielfeldt\Iterators\UniqueIterator;
$input = new \ArrayIterator([-4, -3, -2, -1, 0, 1, 2, 3, 5]);
// Unique elements by their square.
$iterator = new UniqueIterator($input, UniqueIterator::REINDEX, function ($iterator) {
return $iterator->current() * $iterator->current();
});
print_r(iterator_to_array($iterator));
Output:
Array
(
[0] => -4
[1] => -3
[2] => -2
[3] => -1
[4] => 0
[8] => 5
)
Similar to array_vales().
use Gielfeldt\Iterators\ValuesIterator;
$input = new \ArrayIterator([
'key1' => 'value1',
'key2' => 'value2',
'key3' => 'value3',
]);
$iterator = new ValuesIterator($input);
print_r(iterator_to_array($iterator));
Output:
Array
(
[0] => value1
[1] => value2
[2] => value3
)
These are more an extension of the SPL file handling.
An extension of SplFileObject in csv mode, but with csv header support.
use Gielfeldt\Iterators\CsvFileObject;
// Load csv file and dump it.
$file = new CsvFileObject('somefile.csv');
print_r(iterator_to_array($file));
// Same but csv comes via a string variable.
$csvdata = "Columm1,Column2\nValue1,Value2\nValue3,Value4";
$file = new CsvFileObject('data://application/octet,' . $csvdata);
print_r(iterator_to_array($file));
Output:
Array
(
[0] => Array
(
[Columm1] => Value1
[Column2] => Value2
)
[1] => Array
(
[Columm1] => Value3
[Column2] => Value4
)
)
Array
(
[0] => Array
(
[Columm1] => Value1
[Column2] => Value2
)
[1] => Array
(
[Columm1] => Value3
[Column2] => Value4
)
)
Contains various helper methods.
use Gielfeldt\Iterators\Iterator;
$input = new \ArrayIterator([1,2,3,4,5,6]);
var_dump(Iterator::sum($input));
var_dump(Iterator::product($input));
var_dump(Iterator::average($input));
var_dump(Iterator::min($input));
var_dump(Iterator::max($input));
Output:
int(21)
int(720)
double(3.5)
int(1)
int(6)
- Lots probably.