Skip to content

Commit

Permalink
added Arrays::mapWithKeys() & Iterables::mapWithKeys()
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Aug 7, 2024
1 parent c7ae954 commit 09d980b
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/Utils/Arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,31 @@ public static function map(iterable $array, callable $transformer): array
}


/**
* Returns an array containing new keys and values generated by applying the given transform function to each element.
* If the function returns null, the element is skipped.
* @template K of int|string
* @template V
* @template ResK of int|string
* @template ResV
* @param array<K, V> $array
* @param callable(V, K, array<K, V>): ?array{ResK, ResV} $transformer
* @return array<ResK, ResV>
*/
public static function mapWithKeys(array $array, callable $transformer): array
{
$res = [];
foreach ($array as $k => $v) {
$pair = $transformer($v, $k, $array);
if ($pair) {
$res[$pair[0]] = $pair[1];
}
}

return $res;
}


/**
* Invokes all callbacks and returns array of results.
* @param callable[] $callbacks
Expand Down
21 changes: 21 additions & 0 deletions src/Utils/Iterables.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,27 @@ public static function map(iterable $iterable, callable $transformer): \Generato
}


/**
* Iterator that transforms keys and values by calling $transformer. If it returns null, the element is skipped.
* @template K
* @template V
* @template ResV
* @template ResK
* @param iterable<K, V> $iterable
* @param callable(V, K, iterable<K, V>): ?array{ResV, ResK} $transformer
* @return \Generator<ResV, ResK>
*/
public static function mapWithKeys(iterable $iterable, callable $transformer): \Generator
{
foreach ($iterable as $k => $v) {
$pair = $transformer($v, $k, $iterable);
if ($pair) {
yield $pair[0] => $pair[1];
}
}
}


/**
* Wraps around iterator and caches its keys and values during iteration.
* This allows the data to be re-iterated multiple times.
Expand Down
65 changes: 65 additions & 0 deletions tests/Utils/Arrays.mapWithKeys().phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

/**
* Test: Nette\Utils\Arrays::mapWithKeys()
*/

declare(strict_types=1);

use Nette\Utils\Arrays;
use Tester\Assert;


require __DIR__ . '/../bootstrap.php';


test('empty array', function () {
$arr = [];
$log = [];
$res = Arrays::mapWithKeys(
$arr,
function ($v, $k, $arr) use (&$log) {
$log[] = func_get_args();
return [];
},
);
Assert::same([], $res);
Assert::same([], $log);
});

test('list', function () {
$arr = ['a', 'b'];
$log = [];
$res = Arrays::mapWithKeys(
$arr,
function ($v, $k, $arr) use (&$log) {
$log[] = func_get_args();
return ["_$k", "_$v"];
},
);
Assert::same(['_0' => '_a', '_1' => '_b'], $res);
Assert::same([['a', 0, $arr], ['b', 1, $arr]], $log);
});

test('array with keys', function () {
$arr = ['x' => 'a', 'y' => 'b'];
$log = [];
$res = Arrays::mapWithKeys(
$arr,
function ($v, $k, $arr) use (&$log) {
$log[] = func_get_args();
return ["_$k", "_$v"];
},
);
Assert::same(['_x' => '_a', '_y' => '_b'], $res);
Assert::same([['a', 'x', $arr], ['b', 'y', $arr]], $log);
});

test('skipped elements', function () {
$arr = ['x' => 'a', 'y' => 'b', 'z' => 'c'];
$res = Arrays::mapWithKeys(
$arr,
fn($v, $k) => $k === 'y' ? null : ["_$k", "_$v"],
);
Assert::same(['_x' => '_a', '_z' => '_c'], $res);
});
50 changes: 50 additions & 0 deletions tests/Utils/Iterables.mapWithKeys().phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

/**
* Test: Nette\Utils\Iterables::mapWithKeys()
*/

declare(strict_types=1);

use Nette\Utils\Iterables;
use Tester\Assert;

require __DIR__ . '/../bootstrap.php';


test('empty iterable', function () {
$arr = new ArrayIterator([]);
$log = [];
$res = Iterables::mapWithKeys(
$arr,
function ($v, $k, $arr) use (&$log) {
$log[] = func_get_args();
return [];
},
);
Assert::same([], iterator_to_array($res));
Assert::same([], $log);
});

test('non-empty iterable', function () {
$arr = new ArrayIterator(['x' => 'a', 'y' => 'b']);
$log = [];
$res = Iterables::mapWithKeys(
$arr,
function ($v, $k, $arr) use (&$log) {
$log[] = func_get_args();
return ["_$k", "_$v"];
},
);
Assert::same(['_x' => '_a', '_y' => '_b'], iterator_to_array($res));
Assert::same([['a', 'x', $arr], ['b', 'y', $arr]], $log);
});

test('skipped elements', function () {
$arr = new ArrayIterator(['x' => 'a', 'y' => 'b', 'z' => 'c']);
$res = Iterables::mapWithKeys(
$arr,
fn($v, $k) => $k === 'y' ? null : ["_$k", "_$v"],
);
Assert::same(['_x' => '_a', '_z' => '_c'], iterator_to_array($res));
});

0 comments on commit 09d980b

Please sign in to comment.