Skip to content

Commit 09d980b

Browse files
committed
added Arrays::mapWithKeys() & Iterables::mapWithKeys()
1 parent c7ae954 commit 09d980b

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed

src/Utils/Arrays.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,31 @@ public static function map(iterable $array, callable $transformer): array
455455
}
456456

457457

458+
/**
459+
* Returns an array containing new keys and values generated by applying the given transform function to each element.
460+
* If the function returns null, the element is skipped.
461+
* @template K of int|string
462+
* @template V
463+
* @template ResK of int|string
464+
* @template ResV
465+
* @param array<K, V> $array
466+
* @param callable(V, K, array<K, V>): ?array{ResK, ResV} $transformer
467+
* @return array<ResK, ResV>
468+
*/
469+
public static function mapWithKeys(array $array, callable $transformer): array
470+
{
471+
$res = [];
472+
foreach ($array as $k => $v) {
473+
$pair = $transformer($v, $k, $array);
474+
if ($pair) {
475+
$res[$pair[0]] = $pair[1];
476+
}
477+
}
478+
479+
return $res;
480+
}
481+
482+
458483
/**
459484
* Invokes all callbacks and returns array of results.
460485
* @param callable[] $callbacks

src/Utils/Iterables.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,27 @@ public static function map(iterable $iterable, callable $transformer): \Generato
156156
}
157157

158158

159+
/**
160+
* Iterator that transforms keys and values by calling $transformer. If it returns null, the element is skipped.
161+
* @template K
162+
* @template V
163+
* @template ResV
164+
* @template ResK
165+
* @param iterable<K, V> $iterable
166+
* @param callable(V, K, iterable<K, V>): ?array{ResV, ResK} $transformer
167+
* @return \Generator<ResV, ResK>
168+
*/
169+
public static function mapWithKeys(iterable $iterable, callable $transformer): \Generator
170+
{
171+
foreach ($iterable as $k => $v) {
172+
$pair = $transformer($v, $k, $iterable);
173+
if ($pair) {
174+
yield $pair[0] => $pair[1];
175+
}
176+
}
177+
}
178+
179+
159180
/**
160181
* Wraps around iterator and caches its keys and values during iteration.
161182
* This allows the data to be re-iterated multiple times.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\Utils\Arrays::mapWithKeys()
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\Utils\Arrays;
10+
use Tester\Assert;
11+
12+
13+
require __DIR__ . '/../bootstrap.php';
14+
15+
16+
test('empty array', function () {
17+
$arr = [];
18+
$log = [];
19+
$res = Arrays::mapWithKeys(
20+
$arr,
21+
function ($v, $k, $arr) use (&$log) {
22+
$log[] = func_get_args();
23+
return [];
24+
},
25+
);
26+
Assert::same([], $res);
27+
Assert::same([], $log);
28+
});
29+
30+
test('list', function () {
31+
$arr = ['a', 'b'];
32+
$log = [];
33+
$res = Arrays::mapWithKeys(
34+
$arr,
35+
function ($v, $k, $arr) use (&$log) {
36+
$log[] = func_get_args();
37+
return ["_$k", "_$v"];
38+
},
39+
);
40+
Assert::same(['_0' => '_a', '_1' => '_b'], $res);
41+
Assert::same([['a', 0, $arr], ['b', 1, $arr]], $log);
42+
});
43+
44+
test('array with keys', function () {
45+
$arr = ['x' => 'a', 'y' => 'b'];
46+
$log = [];
47+
$res = Arrays::mapWithKeys(
48+
$arr,
49+
function ($v, $k, $arr) use (&$log) {
50+
$log[] = func_get_args();
51+
return ["_$k", "_$v"];
52+
},
53+
);
54+
Assert::same(['_x' => '_a', '_y' => '_b'], $res);
55+
Assert::same([['a', 'x', $arr], ['b', 'y', $arr]], $log);
56+
});
57+
58+
test('skipped elements', function () {
59+
$arr = ['x' => 'a', 'y' => 'b', 'z' => 'c'];
60+
$res = Arrays::mapWithKeys(
61+
$arr,
62+
fn($v, $k) => $k === 'y' ? null : ["_$k", "_$v"],
63+
);
64+
Assert::same(['_x' => '_a', '_z' => '_c'], $res);
65+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
/**
4+
* Test: Nette\Utils\Iterables::mapWithKeys()
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
use Nette\Utils\Iterables;
10+
use Tester\Assert;
11+
12+
require __DIR__ . '/../bootstrap.php';
13+
14+
15+
test('empty iterable', function () {
16+
$arr = new ArrayIterator([]);
17+
$log = [];
18+
$res = Iterables::mapWithKeys(
19+
$arr,
20+
function ($v, $k, $arr) use (&$log) {
21+
$log[] = func_get_args();
22+
return [];
23+
},
24+
);
25+
Assert::same([], iterator_to_array($res));
26+
Assert::same([], $log);
27+
});
28+
29+
test('non-empty iterable', function () {
30+
$arr = new ArrayIterator(['x' => 'a', 'y' => 'b']);
31+
$log = [];
32+
$res = Iterables::mapWithKeys(
33+
$arr,
34+
function ($v, $k, $arr) use (&$log) {
35+
$log[] = func_get_args();
36+
return ["_$k", "_$v"];
37+
},
38+
);
39+
Assert::same(['_x' => '_a', '_y' => '_b'], iterator_to_array($res));
40+
Assert::same([['a', 'x', $arr], ['b', 'y', $arr]], $log);
41+
});
42+
43+
test('skipped elements', function () {
44+
$arr = new ArrayIterator(['x' => 'a', 'y' => 'b', 'z' => 'c']);
45+
$res = Iterables::mapWithKeys(
46+
$arr,
47+
fn($v, $k) => $k === 'y' ? null : ["_$k", "_$v"],
48+
);
49+
Assert::same(['_x' => '_a', '_z' => '_c'], iterator_to_array($res));
50+
});

0 commit comments

Comments
 (0)