Skip to content

Commit 6e0498c

Browse files
authored
Add a tap function (#95)
This function lazily performs a side effect for each item in an iterable, without changing the keys/values of said iterable. This is useful for things like logging, saving partial results to a database, or any other side effects which should not change the outcome of the full iteration pipeline.
1 parent d9f88bc commit 6e0498c

File tree

3 files changed

+57
-0
lines changed

3 files changed

+57
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ list the function signatures as an overview:
6767
Iterator flip(iterable $iterable)
6868
Iterator chunk(iterable $iterable, int $size, bool $preserveKeys = false)
6969
Iterator chunkWithKeys(iterable $iterable, int $size)
70+
Iterator tap(callable $function, iterable $iterable)
7071
Iterator toIter(iterable $iterable)
7172

7273
Iterator range(number $start, number $end, number $step = null)

src/iter.php

+33
Original file line numberDiff line numberDiff line change
@@ -1257,6 +1257,39 @@ function toArrayWithKeys(iterable $iterable): array {
12571257
function isIterable($value) {
12581258
return is_array($value) || $value instanceof \Traversable;
12591259
}
1260+
1261+
/**
1262+
* Lazily performs a side effect for each value in an iterable.
1263+
*
1264+
* The passed function is called with the value and key of each element of the
1265+
* iterable. The return value of the function is ignored.
1266+
*
1267+
* Examples:
1268+
* $iterable = iter\range(1, 3);
1269+
* // => iterable(1, 2, 3)
1270+
*
1271+
* $iterable = iter\tap(function($value, $key) { echo $value; }, $iterable);
1272+
* // => iterable(1, 2, 3) : pending side effects
1273+
*
1274+
* iter\toArray($iterable);
1275+
* // => [1, 2, 3]
1276+
* // "123" : side effects were executed
1277+
*
1278+
* @template TKey
1279+
* @template TValue
1280+
*
1281+
* @param callable(TValue, TKey):void $function A function to call for each value as a side effect
1282+
* @param iterable<TKey, TValue> $iterable The iterable to tap
1283+
*
1284+
* @return iterable<TKey, TValue>
1285+
*/
1286+
function tap(callable $function, iterable $iterable): \Iterator {
1287+
foreach ($iterable as $key => $value) {
1288+
$function($value, $key);
1289+
yield $key => $value;
1290+
}
1291+
}
1292+
12601293
/*
12611294
* Python:
12621295
* compress()

test/iterTest.php

+23
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,29 @@ function() {
618618
\TypeError::class
619619
];
620620
}
621+
622+
public function testTap() {
623+
# Simple case where callback does not return anything
624+
$this->assertSame(
625+
[1, 2, 3],
626+
toArray(tap(function() {}, [1, 2, 3]))
627+
);
628+
629+
# Should also not care about the return value of the callback
630+
# Also, check that this is called once for every element in the iterable
631+
$mock = $this->getMockBuilder(\stdClass::class)
632+
->setMethods(['foo'])
633+
->getMock();
634+
635+
$mock->expects($this->exactly(3))
636+
->method('foo')
637+
->will($this->returnArgument(42));
638+
639+
$this->assertSame(
640+
[1, 2, 3],
641+
toArray(tap([$mock, 'foo'], [1, 2, 3]))
642+
);
643+
}
621644
}
622645

623646
class _CountableTestDummy implements \Countable {

0 commit comments

Comments
 (0)