Skip to content

Commit 43b5ed0

Browse files
committed
Arrays::first() & last(): added parameter $predicate [Closes #305]
thx @into-the-v0id
1 parent 410e508 commit 43b5ed0

File tree

3 files changed

+67
-25
lines changed

3 files changed

+67
-25
lines changed

src/Utils/Arrays.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,26 +121,30 @@ public static function contains(array $array, mixed $value): bool
121121

122122

123123
/**
124-
* Returns the first item from the array or null if array is empty.
124+
* Returns the first item from the array (matching the specified predicate if given) or null if there is no such item.
125+
* The $predicate has the signature `function (mixed $value, int|string $key, array $array): bool`.
125126
* @template T
126127
* @param array<T> $array
127128
* @return ?T
128129
*/
129-
public static function first(array $array): mixed
130+
public static function first(array $array, ?callable $predicate = null): mixed
130131
{
131-
return $array[array_key_first($array)] ?? null;
132+
$key = self::firstKey($array, $predicate);
133+
return $key === null ? null : $array[$key];
132134
}
133135

134136

135137
/**
136-
* Returns the last item from the array or null if array is empty.
138+
* Returns the last item from the array (matching the specified predicate if given) or null if there is no such item.
139+
* The $predicate has the signature `function (mixed $value, int|string $key, array $array): bool`.
137140
* @template T
138141
* @param array<T> $array
139142
* @return ?T
140143
*/
141-
public static function last(array $array): mixed
144+
public static function last(array $array, ?callable $predicate = null): mixed
142145
{
143-
return $array[array_key_last($array)] ?? null;
146+
$key = self::lastKey($array, $predicate);
147+
return $key === null ? null : $array[$key];
144148
}
145149

146150

tests/Utils/Arrays.first().phpt

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,32 @@ use Tester\Assert;
99
require __DIR__ . '/../bootstrap.php';
1010

1111

12-
Assert::null(Arrays::first([]));
13-
Assert::null(Arrays::first([null]));
14-
Assert::false(Arrays::first([false]));
15-
Assert::same(1, Arrays::first([1, 2, 3]));
16-
17-
18-
$arr = [1, 2, 3];
19-
end($arr);
20-
Assert::same(1, Arrays::first($arr));
21-
Assert::same(3, current($arr));
12+
test('no predicate', function () {
13+
Assert::null(Arrays::first([]));
14+
Assert::null(Arrays::first([null]));
15+
Assert::false(Arrays::first([false]));
16+
Assert::same(1, Arrays::first([1, 2, 3]));
17+
});
18+
19+
test('internal array pointer is not affected', function () {
20+
$arr = [1, 2, 3];
21+
end($arr);
22+
Assert::same(1, Arrays::first($arr));
23+
Assert::same(3, current($arr));
24+
});
25+
26+
test('with predicate', function () {
27+
Assert::null(Arrays::first([], fn() => true));
28+
Assert::null(Arrays::first([], fn() => false));
29+
Assert::null(Arrays::first(['' => 'x'], fn() => false));
30+
Assert::null(Arrays::first([null], fn() => true));
31+
Assert::null(Arrays::first([null], fn() => false));
32+
Assert::same(1, Arrays::first([1, 2, 3], fn() => true));
33+
Assert::null(Arrays::first([1, 2, 3], fn() => false));
34+
Assert::same(3, Arrays::first([1, 2, 3], fn($v) => $v > 2));
35+
Assert::same(1, Arrays::first([1, 2, 3], fn($v) => $v < 2));
36+
});
37+
38+
test('predicate arguments', function () {
39+
Arrays::first([2 => 'x'], fn() => Assert::same(['x', 2, [2 => 'x']], func_get_args()));
40+
});

tests/Utils/Arrays.last().phpt

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,31 @@ use Tester\Assert;
99
require __DIR__ . '/../bootstrap.php';
1010

1111

12-
Assert::null(Arrays::last([]));
13-
Assert::null(Arrays::last([null]));
14-
Assert::false(Arrays::last([false]));
15-
Assert::same(3, Arrays::last([1, 2, 3]));
16-
17-
18-
$arr = [1, 2, 3];
19-
Assert::same(3, Arrays::last($arr));
20-
Assert::same(1, current($arr));
12+
test('no predicate', function () {
13+
Assert::null(Arrays::last([]));
14+
Assert::null(Arrays::last([null]));
15+
Assert::false(Arrays::last([false]));
16+
Assert::same(3, Arrays::last([1, 2, 3]));
17+
});
18+
19+
test('internal array pointer is not affected', function () {
20+
$arr = [1, 2, 3];
21+
Assert::same(3, Arrays::last($arr));
22+
Assert::same(1, current($arr));
23+
});
24+
25+
test('with predicate', function () {
26+
Assert::null(Arrays::last([], fn() => true));
27+
Assert::null(Arrays::last([], fn() => false));
28+
Assert::null(Arrays::last(['' => 'x'], fn() => false));
29+
Assert::null(Arrays::last([null], fn() => true));
30+
Assert::null(Arrays::last([null], fn() => false));
31+
Assert::same(3, Arrays::last([1, 2, 3], fn() => true));
32+
Assert::null(Arrays::last([1, 2, 3], fn() => false));
33+
Assert::same(3, Arrays::last([1, 2, 3], fn($v) => $v > 2));
34+
Assert::same(1, Arrays::last([1, 2, 3], fn($v) => $v < 2));
35+
});
36+
37+
test('predicate arguments', function () {
38+
Arrays::last([2 => 'x'], fn() => Assert::same(['x', 2, [2 => 'x']], func_get_args()));
39+
});

0 commit comments

Comments
 (0)