Skip to content

Commit

Permalink
Arrays::first() & last(): added parameter $predicate [Closes #305]
Browse files Browse the repository at this point in the history
  • Loading branch information
dg committed Nov 30, 2023
1 parent 410e508 commit 43b5ed0
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 25 deletions.
16 changes: 10 additions & 6 deletions src/Utils/Arrays.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,26 +121,30 @@ public static function contains(array $array, mixed $value): bool


/**
* Returns the first item from the array or null if array is empty.
* Returns the first item from the array (matching the specified predicate if given) or null if there is no such item.
* The $predicate has the signature `function (mixed $value, int|string $key, array $array): bool`.
* @template T
* @param array<T> $array
* @return ?T
*/
public static function first(array $array): mixed
public static function first(array $array, ?callable $predicate = null): mixed
{
return $array[array_key_first($array)] ?? null;
$key = self::firstKey($array, $predicate);
return $key === null ? null : $array[$key];
}


/**
* Returns the last item from the array or null if array is empty.
* Returns the last item from the array (matching the specified predicate if given) or null if there is no such item.
* The $predicate has the signature `function (mixed $value, int|string $key, array $array): bool`.
* @template T
* @param array<T> $array
* @return ?T
*/
public static function last(array $array): mixed
public static function last(array $array, ?callable $predicate = null): mixed
{
return $array[array_key_last($array)] ?? null;
$key = self::lastKey($array, $predicate);
return $key === null ? null : $array[$key];
}


Expand Down
39 changes: 29 additions & 10 deletions tests/Utils/Arrays.first().phpt
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,32 @@ use Tester\Assert;
require __DIR__ . '/../bootstrap.php';


Assert::null(Arrays::first([]));
Assert::null(Arrays::first([null]));
Assert::false(Arrays::first([false]));
Assert::same(1, Arrays::first([1, 2, 3]));


$arr = [1, 2, 3];
end($arr);
Assert::same(1, Arrays::first($arr));
Assert::same(3, current($arr));
test('no predicate', function () {
Assert::null(Arrays::first([]));
Assert::null(Arrays::first([null]));
Assert::false(Arrays::first([false]));
Assert::same(1, Arrays::first([1, 2, 3]));
});

test('internal array pointer is not affected', function () {
$arr = [1, 2, 3];
end($arr);
Assert::same(1, Arrays::first($arr));
Assert::same(3, current($arr));
});

test('with predicate', function () {
Assert::null(Arrays::first([], fn() => true));
Assert::null(Arrays::first([], fn() => false));
Assert::null(Arrays::first(['' => 'x'], fn() => false));
Assert::null(Arrays::first([null], fn() => true));
Assert::null(Arrays::first([null], fn() => false));
Assert::same(1, Arrays::first([1, 2, 3], fn() => true));
Assert::null(Arrays::first([1, 2, 3], fn() => false));
Assert::same(3, Arrays::first([1, 2, 3], fn($v) => $v > 2));
Assert::same(1, Arrays::first([1, 2, 3], fn($v) => $v < 2));
});

test('predicate arguments', function () {
Arrays::first([2 => 'x'], fn() => Assert::same(['x', 2, [2 => 'x']], func_get_args()));
});
37 changes: 28 additions & 9 deletions tests/Utils/Arrays.last().phpt
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,31 @@ use Tester\Assert;
require __DIR__ . '/../bootstrap.php';


Assert::null(Arrays::last([]));
Assert::null(Arrays::last([null]));
Assert::false(Arrays::last([false]));
Assert::same(3, Arrays::last([1, 2, 3]));


$arr = [1, 2, 3];
Assert::same(3, Arrays::last($arr));
Assert::same(1, current($arr));
test('no predicate', function () {
Assert::null(Arrays::last([]));
Assert::null(Arrays::last([null]));
Assert::false(Arrays::last([false]));
Assert::same(3, Arrays::last([1, 2, 3]));
});

test('internal array pointer is not affected', function () {
$arr = [1, 2, 3];
Assert::same(3, Arrays::last($arr));
Assert::same(1, current($arr));
});

test('with predicate', function () {
Assert::null(Arrays::last([], fn() => true));
Assert::null(Arrays::last([], fn() => false));
Assert::null(Arrays::last(['' => 'x'], fn() => false));
Assert::null(Arrays::last([null], fn() => true));
Assert::null(Arrays::last([null], fn() => false));
Assert::same(3, Arrays::last([1, 2, 3], fn() => true));
Assert::null(Arrays::last([1, 2, 3], fn() => false));
Assert::same(3, Arrays::last([1, 2, 3], fn($v) => $v > 2));
Assert::same(1, Arrays::last([1, 2, 3], fn($v) => $v < 2));
});

test('predicate arguments', function () {
Arrays::last([2 => 'x'], fn() => Assert::same(['x', 2, [2 => 'x']], func_get_args()));
});

0 comments on commit 43b5ed0

Please sign in to comment.