Skip to content

Commit

Permalink
Introduce yield_groups()
Browse files Browse the repository at this point in the history
  • Loading branch information
lippserd committed Aug 10, 2023
1 parent 8bd5759 commit 1627690
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 1 deletion.
40 changes: 40 additions & 0 deletions src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

namespace ipl\Stdlib;

use Generator;
use InvalidArgumentException;
use Iterator;
use Traversable;
use stdClass;

Expand Down Expand Up @@ -69,3 +71,41 @@ function iterable_key_first($iterable)

return null;
}

/**
* Yield sets of items from an iterator grouped by a specific criterion gathered from a callback
*
* The iterator must be sorted by the criterion. The callback must return at least the criterion,
* but can also return value and key in addition.
*
* @param Iterator $iterator
* @param callable(mixed $value, mixed $key): array{0: mixed, 1?: mixed, 2?: mixed} $groupBy
*
* @return Generator
*/
function yield_groups(Iterator $iterator, callable $groupBy): Generator
{
if (! $iterator->valid()) {
return;
}

list($criterion, $v, $k) = array_pad((array) $groupBy($iterator->current(), $iterator->key()), 3, null);
$group = [$k ?? $iterator->key() => $v ?? $iterator->current()];

$iterator->next();
for (; $iterator->valid(); $iterator->next()) {
list($c, $v, $k) = array_pad((array) $groupBy($iterator->current(), $iterator->key()), 3, null);
if ($c !== $criterion) {
yield $criterion => $group;

$group = [];
$criterion = $c;
}

$group[$k ?? $iterator->key()] = $v ?? $iterator->current();
}

if (! empty($group)) {
yield $criterion => $group;
}
}
148 changes: 147 additions & 1 deletion tests/FunctionsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,44 @@
namespace ipl\Tests\Stdlib;

use ArrayIterator;
use EmptyIterator;
use InvalidArgumentException;
use stdClass;
use ipl\Stdlib;
use stdClass;

class FunctionsTest extends TestCase
{
protected const YIELD_GROUPS_DATA = [
'one' => [
'group' => 1,
'id' => 1
],
'two' => [
'group' => 1,
'id' => 2
],
'three' => [
'group' => 1,
'id' => 3
],
'four' => [
'group' => 2,
'id' => 4
],
'five' => [
'group' => 3,
'id' => 5
],
'six' => [
'group' => 3,
'id' => 6
],
'seven' => [
'group' => 3,
'id' => 7
]
];

public function testGetPhpTypeWithObject()
{
$object = (object) [];
Expand Down Expand Up @@ -98,4 +130,118 @@ public function testIterableKeyFirstReturnsNullIfIterableIsGeneratorAndIsEmpty()
yield;
})));
}

public function testYieldGroupsWithEmptyIterator()
{
$this->assertEquals([], iterator_to_array(Stdlib\yield_groups(new EmptyIterator(), function () {
})));
}

public function testYieldGroupsCallbackArguments()
{
Stdlib\yield_groups(new ArrayIterator(static::YIELD_GROUPS_DATA), function (array $v, string $k): int {
$this->assertEquals(static::YIELD_GROUPS_DATA[$k], $v);

return $v['group'];
});
}

public function testYieldGroupsWithCallbackReturningCriterion()
{
$this->assertEquals(
[
1 => [
'one' => [
'group' => 1,
'id' => 1
],
'two' => [
'group' => 1,
'id' => 2
],
'three' => [
'group' => 1,
'id' => 3
]
],
2 => [
'four' => [
'group' => 2,
'id' => 4
]
],
3 => [
'five' => [
'group' => 3,
'id' => 5
],
'six' => [
'group' => 3,
'id' => 6
],
'seven' => [
'group' => 3,
'id' => 7
]
]
],
iterator_to_array(
Stdlib\yield_groups(new ArrayIterator(static::YIELD_GROUPS_DATA), function (array $v): int {
return $v['group'];
})
)
);
}

public function testYieldGroupsWithCallbackReturningCriterionAndValue()
{
$this->assertEquals(
[
1 => [
'one' => 1,
'two' => 2,
'three' => 3
],
2 => [
'four' => 4,
],
3 => [
'five' => 5,
'six' => 6,
'seven' => 7
]
],
iterator_to_array(
Stdlib\yield_groups(new ArrayIterator(static::YIELD_GROUPS_DATA), function (array $v): array {
return [$v['group'], $v['id']];
})
)
);
}

public function testYieldGroupsWithCallbackReturningCriterionValueAndKey()
{
$this->assertEquals(
[
1 => [
1 => 1,
2 => 2,
3 => 3
],
2 => [
4 => 4
],
3 => [
5 => 5,
6 => 6,
7 => 7
]
],
iterator_to_array(
Stdlib\yield_groups(new ArrayIterator(static::YIELD_GROUPS_DATA), function (array $v): array {
return [$v['group'], $v['id'], $v['id']];
})
)
);
}
}

0 comments on commit 1627690

Please sign in to comment.