Skip to content

Commit bc2ab17

Browse files
committed
added Arrays::mapWithKeys() & Iterables::mapWithKeys()
1 parent 539fbcb commit bc2ab17

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
@@ -446,6 +446,31 @@ public static function map(iterable $array, callable $transformer): array
446446
}
447447

448448

449+
/**
450+
* Returns an array containing new keys and values generated by applying the given transform function to each element.
451+
* If the function returns null, the element is skipped.
452+
* @template K of int|string
453+
* @template V
454+
* @template ResultK of int|string
455+
* @template ResultV
456+
* @param array<K, V> $array
457+
* @param callable(V, K, array<K, V>): ?array{ResultK, ResultV} $transformer
458+
* @return array<ResultK, ResultV>
459+
*/
460+
public static function mapWithKeys(array $array, callable $transformer): array
461+
{
462+
$res = [];
463+
foreach ($array as $k => $v) {
464+
$pair = $transformer($v, $k, $array);
465+
if ($pair) {
466+
$res[$pair[0]] = $pair[1];
467+
}
468+
}
469+
470+
return $res;
471+
}
472+
473+
449474
/**
450475
* Invokes all callbacks and returns array of results.
451476
* @param callable[] $callbacks

src/Utils/Iterables.php

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

160160

161+
/**
162+
* Iterator that transforms keys and values by calling $transformer. If it returns null, the element is skipped.
163+
* @template K
164+
* @template V
165+
* @template ResultK
166+
* @template ResultV
167+
* @param iterable<K, V> $iterable
168+
* @param callable(V, K, iterable<K, V>): ?array{ResultK, ResultV} $transformer
169+
* @return \Generator<ResultK, ResultV>
170+
*/
171+
public static function mapWithKeys(iterable $iterable, callable $transformer): \Generator
172+
{
173+
foreach ($iterable as $k => $v) {
174+
$pair = $transformer($v, $k, $iterable);
175+
if ($pair) {
176+
yield $pair[0] => $pair[1];
177+
}
178+
}
179+
}
180+
181+
161182
/**
162183
* Wraps around iterator and caches its keys and values during iteration.
163184
* This allows the data to be re-iterated multiple times.

tests/Utils/Arrays.mapWithKeys().phpt

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)