Skip to content

Commit

Permalink
Add a tap function (#95)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
athrawes committed Oct 11, 2023
1 parent d9f88bc commit 6e0498c
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ list the function signatures as an overview:
Iterator flip(iterable $iterable)
Iterator chunk(iterable $iterable, int $size, bool $preserveKeys = false)
Iterator chunkWithKeys(iterable $iterable, int $size)
Iterator tap(callable $function, iterable $iterable)
Iterator toIter(iterable $iterable)

Iterator range(number $start, number $end, number $step = null)
Expand Down
33 changes: 33 additions & 0 deletions src/iter.php
Original file line number Diff line number Diff line change
Expand Up @@ -1257,6 +1257,39 @@ function toArrayWithKeys(iterable $iterable): array {
function isIterable($value) {
return is_array($value) || $value instanceof \Traversable;
}

/**
* Lazily performs a side effect for each value in an iterable.
*
* The passed function is called with the value and key of each element of the
* iterable. The return value of the function is ignored.
*
* Examples:
* $iterable = iter\range(1, 3);
* // => iterable(1, 2, 3)
*
* $iterable = iter\tap(function($value, $key) { echo $value; }, $iterable);
* // => iterable(1, 2, 3) : pending side effects
*
* iter\toArray($iterable);
* // => [1, 2, 3]
* // "123" : side effects were executed
*
* @template TKey
* @template TValue
*
* @param callable(TValue, TKey):void $function A function to call for each value as a side effect
* @param iterable<TKey, TValue> $iterable The iterable to tap
*
* @return iterable<TKey, TValue>
*/
function tap(callable $function, iterable $iterable): \Iterator {
foreach ($iterable as $key => $value) {
$function($value, $key);
yield $key => $value;
}
}

/*
* Python:
* compress()
Expand Down
23 changes: 23 additions & 0 deletions test/iterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,29 @@ function() {
\TypeError::class
];
}

public function testTap() {
# Simple case where callback does not return anything
$this->assertSame(
[1, 2, 3],
toArray(tap(function() {}, [1, 2, 3]))
);

# Should also not care about the return value of the callback
# Also, check that this is called once for every element in the iterable
$mock = $this->getMockBuilder(\stdClass::class)
->setMethods(['foo'])
->getMock();

$mock->expects($this->exactly(3))
->method('foo')
->will($this->returnArgument(42));

$this->assertSame(
[1, 2, 3],
toArray(tap([$mock, 'foo'], [1, 2, 3]))
);
}
}

class _CountableTestDummy implements \Countable {
Expand Down

0 comments on commit 6e0498c

Please sign in to comment.