From dd104201a6afbdb4fa8e215315194b9a6e4d3e90 Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Wed, 16 Oct 2024 16:37:32 +0200 Subject: [PATCH 01/17] :rotating_light: code styles --- rector.php | 1 - 1 file changed, 1 deletion(-) diff --git a/rector.php b/rector.php index 64bff3315..f20cb777d 100644 --- a/rector.php +++ b/rector.php @@ -8,7 +8,6 @@ use Rector\Config\RectorConfig; use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedPublicMethodParameterRector; use Rector\DeadCode\Rector\PropertyProperty\RemoveNullPropertyInitializationRector; -use Rector\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector; use Rector\Php70\Rector\StaticCall\StaticCallOnNonStaticToInstanceCallRector; use Rector\Php74\Rector\Closure\ClosureToArrowFunctionRector; use Rector\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector; From b014180e8efff4043fc0587b7aebf9cbc4ff3326 Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Wed, 16 Oct 2024 16:51:46 +0200 Subject: [PATCH 02/17] :sparkles: Add method remove() --- src/Tempest/Support/src/ArrayHelper.php | 17 ++++ src/Tempest/Support/tests/ArrayHelperTest.php | 84 +++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index 7e2f82740..46554f0b9 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -48,6 +48,23 @@ public function __construct( } } + /** + * Remove items from the array. + * + * @param array-key|array $keys The keys of the items to remove. + * + * @return self + */ + public function remove(string|int|array $keys): self { + $keys = is_array($keys) ? $keys : [$keys]; + + foreach ($keys as $key) { + $this->offsetUnset($key); + } + + return $this; + } + /** * Determines if the array is a list. * diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index 14d46af1c..08a2ae1f0 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1109,4 +1109,88 @@ public function test_is_assoc(): void $this->assertTrue(arr([0 => 'a', 2 => 'b'])->isAssoc()); $this->assertTrue(arr(['foo' => 'a', 'baz' => 'b'])->isAssoc()); } + + public function test_remove_with_basic_keys(): void { + $collection = arr([1, 2, 3]); + + $this->assertEquals( + actual: $collection + ->remove(1) + ->toArray(), + expected: [ + 0 => 1, + 2 => 3 + ], + ); + + $this->assertEquals( + actual: $collection + ->remove([0, 2]) + ->toArray(), + expected: [], + ); + } + + public function test_remove_with_associative_keys(): void { + $collection = arr([ + 'first_name' => 'John', + 'last_name' => 'Doe', + 'age' => 42, + ]); + + $this->assertEquals( + actual: $collection + ->remove('first_name') + ->toArray(), + expected: [ + 'last_name' => 'Doe', + 'age' => 42, + ], + ); + + $this->assertEquals( + actual: $collection + ->remove(['last_name', 'age']) + ->toArray(), + expected: [], + ); + } + + public function test_remove_with_no_valid_key(): void { + $collection = arr([1, 2, 3]); + + $this->assertEquals( + actual: $collection + ->remove(42) + ->toArray(), + expected: [1, 2, 3], + ); + + $collection = arr([ + 'first_name' => 'John', + 'last_name' => 'Doe', + 'age' => 42, + ]); + + $this->assertEquals( + actual: $collection + ->remove('foo') + ->toArray(), + expected: [ + 'first_name' => 'John', + 'last_name' => 'Doe', + 'age' => 42, + ], + ); + + $this->assertEquals( + actual: $collection + ->remove(['bar', 'first_name']) + ->toArray(), + expected: [ + 'last_name' => 'Doe', + 'age' => 42, + ], + ); + } } From c12ff8dffb6865a223e06f1ad8587c7ad9a10644 Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Wed, 16 Oct 2024 16:54:48 +0200 Subject: [PATCH 03/17] :sparkles: Add method forget() --- src/Tempest/Support/src/ArrayHelper.php | 7 ++++++ src/Tempest/Support/tests/ArrayHelperTest.php | 24 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index 46554f0b9..b20dbd302 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -48,6 +48,13 @@ public function __construct( } } + /** + * @alias of add. + */ + public function forget(string|int|array $keys): self { + return $this->remove($keys); + } + /** * Remove items from the array. * diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index 08a2ae1f0..3becfac31 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1193,4 +1193,28 @@ public function test_remove_with_no_valid_key(): void { ], ); } + + public function test_forget_is_alias_of_remove(): void + { + $first_collection = arr( [ + 'first_name' => 'John', + 'last_name' => 'Doe', + 'age' => 42, + ] ) + ->remove(42) + ->remove('foo') + ->remove(['bar', 'first_name']); + + $second_collection = arr( [ + 'first_name' => 'John', + 'last_name' => 'Doe', + 'age' => 42, + ] ) + ->forget(42) + ->forget('foo') + ->forget(['bar', 'first_name']); + + + $this->assertTrue($first_collection->equals($second_collection)); + } } From e4c2f239684ec0eb0d2a9daa953c5c6368333183 Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Wed, 16 Oct 2024 17:08:57 +0200 Subject: [PATCH 04/17] :sparkles: Add method shuffle() --- src/Tempest/Support/src/ArrayHelper.php | 9 +++++++++ src/Tempest/Support/tests/ArrayHelperTest.php | 20 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index b20dbd302..b436a77ef 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -48,6 +48,15 @@ public function __construct( } } + /** + * Shuffle the array. + * + * @return self + */ + public function shuffle(): self { + return new self( (new Randomizer)->shuffleArray($this->array) ); + } + /** * @alias of add. */ diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index 3becfac31..90bbbc4c2 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1217,4 +1217,24 @@ public function test_forget_is_alias_of_remove(): void $this->assertTrue($first_collection->equals($second_collection)); } + + public function test_shuffle_actually_shuffles(): void { + $array = range('a', 'z'); + + $this->assertFalse( + arr($array)->shuffle()->toArray() === $array + && arr($array)->shuffle()->toArray() === $array, + ); + } + + public function test_shuffle_keeps_same_values(): void { + $array = range('a', 'z'); + $shuffled = arr($array)->shuffle()->toArray(); + sort($shuffled); + + $this->assertSame( + actual : $shuffled, + expected: $array, + ); + } } From 998e6c0f6d6e0a62c5353aad55eeda3341f82310 Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Wed, 16 Oct 2024 17:17:14 +0200 Subject: [PATCH 05/17] :sparkles: Add method pull() --- src/Tempest/Support/src/ArrayHelper.php | 14 +++++++++++++ src/Tempest/Support/tests/ArrayHelperTest.php | 21 +++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index b436a77ef..200a45e21 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -48,6 +48,20 @@ public function __construct( } } + /** + * Get a value from the array and remove it. + * + * @param array-key $key + * @param mixed $default + */ + public function pull(string|int $key, mixed $default = null): mixed { + $value = $this->get($key, $default); + + $this->remove($key); + + return $value; + } + /** * Shuffle the array. * diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index 90bbbc4c2..ff361aeee 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1237,4 +1237,25 @@ public function test_shuffle_keeps_same_values(): void { expected: $array, ); } + + public function test_pull(): void { + $collection = arr([ + 'first_name' => 'John', + 'last_name' => 'Doe', + 'age' => 42, + ]); + + $this->assertSame( + actual: $collection->pull('first_name'), + expected: 'John', + ); + + $this->assertSame( + actual: $collection->toArray(), + expected: [ + 'last_name' => 'Doe', + 'age' => 42, + ], + ); + } } From 3b423cb05a37b373ab27a6e222799d6896c0c8f7 Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Wed, 16 Oct 2024 18:13:20 +0200 Subject: [PATCH 06/17] :rotating_light: coding styles --- src/Tempest/Support/src/ArrayHelper.php | 16 +++-- src/Tempest/Support/tests/ArrayHelperTest.php | 64 ++++++++++--------- 2 files changed, 45 insertions(+), 35 deletions(-) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index 200a45e21..bd31f06d0 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -54,7 +54,8 @@ public function __construct( * @param array-key $key * @param mixed $default */ - public function pull(string|int $key, mixed $default = null): mixed { + public function pull(string|int $key, mixed $default = null): mixed + { $value = $this->get($key, $default); $this->remove($key); @@ -67,14 +68,16 @@ public function pull(string|int $key, mixed $default = null): mixed { * * @return self */ - public function shuffle(): self { - return new self( (new Randomizer)->shuffleArray($this->array) ); + public function shuffle(): self + { + return new self((new Randomizer())->shuffleArray($this->array)); } /** * @alias of add. */ - public function forget(string|int|array $keys): self { + public function forget(string|int|array $keys): self + { return $this->remove($keys); } @@ -85,9 +88,10 @@ public function forget(string|int|array $keys): self { * * @return self */ - public function remove(string|int|array $keys): self { + public function remove(string|int|array $keys): self + { $keys = is_array($keys) ? $keys : [$keys]; - + foreach ($keys as $key) { $this->offsetUnset($key); } diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index ff361aeee..e5528d5ec 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1110,7 +1110,8 @@ public function test_is_assoc(): void $this->assertTrue(arr(['foo' => 'a', 'baz' => 'b'])->isAssoc()); } - public function test_remove_with_basic_keys(): void { + public function test_remove_with_basic_keys(): void + { $collection = arr([1, 2, 3]); $this->assertEquals( @@ -1119,7 +1120,7 @@ public function test_remove_with_basic_keys(): void { ->toArray(), expected: [ 0 => 1, - 2 => 3 + 2 => 3, ], ); @@ -1131,11 +1132,12 @@ public function test_remove_with_basic_keys(): void { ); } - public function test_remove_with_associative_keys(): void { + public function test_remove_with_associative_keys(): void + { $collection = arr([ 'first_name' => 'John', - 'last_name' => 'Doe', - 'age' => 42, + 'last_name' => 'Doe', + 'age' => 42, ]); $this->assertEquals( @@ -1144,7 +1146,7 @@ public function test_remove_with_associative_keys(): void { ->toArray(), expected: [ 'last_name' => 'Doe', - 'age' => 42, + 'age' => 42, ], ); @@ -1156,7 +1158,8 @@ public function test_remove_with_associative_keys(): void { ); } - public function test_remove_with_no_valid_key(): void { + public function test_remove_with_no_valid_key(): void + { $collection = arr([1, 2, 3]); $this->assertEquals( @@ -1168,8 +1171,8 @@ public function test_remove_with_no_valid_key(): void { $collection = arr([ 'first_name' => 'John', - 'last_name' => 'Doe', - 'age' => 42, + 'last_name' => 'Doe', + 'age' => 42, ]); $this->assertEquals( @@ -1178,8 +1181,8 @@ public function test_remove_with_no_valid_key(): void { ->toArray(), expected: [ 'first_name' => 'John', - 'last_name' => 'Doe', - 'age' => 42, + 'last_name' => 'Doe', + 'age' => 42, ], ); @@ -1188,28 +1191,28 @@ public function test_remove_with_no_valid_key(): void { ->remove(['bar', 'first_name']) ->toArray(), expected: [ - 'last_name' => 'Doe', - 'age' => 42, + 'last_name' => 'Doe', + 'age' => 42, ], ); } public function test_forget_is_alias_of_remove(): void { - $first_collection = arr( [ + $first_collection = arr([ 'first_name' => 'John', - 'last_name' => 'Doe', - 'age' => 42, - ] ) + 'last_name' => 'Doe', + 'age' => 42, + ]) ->remove(42) ->remove('foo') ->remove(['bar', 'first_name']); - - $second_collection = arr( [ + + $second_collection = arr([ 'first_name' => 'John', - 'last_name' => 'Doe', - 'age' => 42, - ] ) + 'last_name' => 'Doe', + 'age' => 42, + ]) ->forget(42) ->forget('foo') ->forget(['bar', 'first_name']); @@ -1218,7 +1221,8 @@ public function test_forget_is_alias_of_remove(): void $this->assertTrue($first_collection->equals($second_collection)); } - public function test_shuffle_actually_shuffles(): void { + public function test_shuffle_actually_shuffles(): void + { $array = range('a', 'z'); $this->assertFalse( @@ -1227,8 +1231,9 @@ public function test_shuffle_actually_shuffles(): void { ); } - public function test_shuffle_keeps_same_values(): void { - $array = range('a', 'z'); + public function test_shuffle_keeps_same_values(): void + { + $array = range('a', 'z'); $shuffled = arr($array)->shuffle()->toArray(); sort($shuffled); @@ -1238,11 +1243,12 @@ public function test_shuffle_keeps_same_values(): void { ); } - public function test_pull(): void { + public function test_pull(): void + { $collection = arr([ 'first_name' => 'John', - 'last_name' => 'Doe', - 'age' => 42, + 'last_name' => 'Doe', + 'age' => 42, ]); $this->assertSame( @@ -1254,7 +1260,7 @@ public function test_pull(): void { actual: $collection->toArray(), expected: [ 'last_name' => 'Doe', - 'age' => 42, + 'age' => 42, ], ); } From 828c6641e998dd2245ba30729be6c4d016b766db Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Wed, 16 Oct 2024 18:13:56 +0200 Subject: [PATCH 07/17] :rotating_light: Run rector --- src/Tempest/Support/src/ArrayHelper.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index bd31f06d0..03e36d849 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -52,7 +52,6 @@ public function __construct( * Get a value from the array and remove it. * * @param array-key $key - * @param mixed $default */ public function pull(string|int $key, mixed $default = null): mixed { From b3a5245bff1c3f11d5bf856e0aaef486d4b35d13 Mon Sep 17 00:00:00 2001 From: Gturpin <34162504+gturpin-dev@users.noreply.github.com> Date: Thu, 17 Oct 2024 08:52:04 +0200 Subject: [PATCH 08/17] :pencil: Fix typo in php doc Co-authored-by: Karel Faille --- src/Tempest/Support/src/ArrayHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index 03e36d849..75215d3cf 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -73,7 +73,7 @@ public function shuffle(): self } /** - * @alias of add. + * @alias of remove. */ public function forget(string|int|array $keys): self { From 9f7ff52134a2a16d5710eb62e4570ea57c55110e Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Tue, 12 Nov 2024 14:04:23 +0100 Subject: [PATCH 09/17] :sparkles: Add method reduce() --- src/Tempest/Support/src/ArrayHelper.php | 21 +++++++++++ src/Tempest/Support/tests/ArrayHelperTest.php | 36 +++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index dedc0ae11..4cea5d6c3 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -44,6 +44,27 @@ public function __construct( } } + /** + * Reduce the array to a single value using a callback. + * + * @template TReduceInitial + * @template TReduceReturnType + * + * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback + * @param TReduceInitial $initial + * + * @return TReduceReturnType + */ + public function reduce(callable $callback, mixed $initial = null): mixed { + $result = $initial; + + foreach ($this->array as $key => $value) { + $result = $callback($result, $value, $key); + } + + return $result; + } + /** * Gets a value from the array and remove it. * diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index 666a0155c..5a819ffe2 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1381,4 +1381,40 @@ public function test_sort_keys_by_callback(): void actual: $array->sortKeysByCallback(fn ($a, $b) => $a <=> $b)->toArray(), ); } + + public function test_basic_reduce(): void + { + $collection = arr([ + 'first_name' => 'John', + 'last_name' => 'Doe', + 'age' => 42, + ]); + + $this->assertSame( + actual: $collection->reduce(fn ($carry, $value) => $carry . ' ' . $value, 'Hello'), + expected: 'Hello John Doe 42', + ); + } + + public function test_reduce_with_existing_function(): void + { + $collection = arr([ + [1, 2, 2, 3], + [2, 3, 3, 4], + [3, 1, 3, 1] + ]); + + $this->assertSame( + actual: $collection->reduce('max'), + expected: [3, 1, 3, 1], + ); + } + + public function test_empty_array_reduce(): void + { + $this->assertSame( + actual: arr()->reduce(fn ($carry, $value) => $carry . ' ' . $value, 'default'), + expected: 'default', + ); + } } From aece43f320177e8a37806f493b5fddbe9478691f Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Tue, 12 Nov 2024 14:15:52 +0100 Subject: [PATCH 10/17] :sparkles: Add method chunk() --- src/Tempest/Support/src/ArrayHelper.php | 23 +++++++ src/Tempest/Support/tests/ArrayHelperTest.php | 63 +++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index 4cea5d6c3..15218a9e4 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -44,6 +44,29 @@ public function __construct( } } + + /** + * Chunk the array into chunks of the given size. + * + * @param integer $size The size of each chunk. + * @param bool $preserveKeys Whether to preserve the keys of the original array. + * + * @return self + */ + public function chunk(int $size, bool $preserveKeys = true): self { + if ( $size <= 0 ) { + return new self; + } + + $chunks = []; + + foreach ( array_chunk($this->array, $size, $preserveKeys) as $chunk ) { + $chunks[] = new self($chunk); + } + + return new self($chunks); + } + /** * Reduce the array to a single value using a callback. * diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index 5a819ffe2..0240faa62 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1417,4 +1417,67 @@ public function test_empty_array_reduce(): void expected: 'default', ); } + + public function test_chunk(): void + { + $collection = arr([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + + $this->assertSame( + actual: $collection + ->chunk(2, preserveKeys: false) + ->map(fn ($chunk) => $chunk->toArray()) + ->toArray(), + expected: [ + [1, 2], + [3, 4], + [5, 6], + [7, 8], + [9, 10], + ], + ); + + $this->assertSame( + actual: $collection + ->chunk(3, preserveKeys: false) + ->map(fn ($chunk) => $chunk->toArray()) + ->toArray(), + expected: [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10], + ], + ); + } + + public function test_chunk_preserve_keys(): void { + $collection = arr([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + + $this->assertSame( + actual: $collection + ->chunk(2) + ->map(fn ($chunk) => $chunk->toArray()) + ->toArray(), + expected: [ + [0 => 1, 1 => 2], + [2 => 3, 3 => 4], + [4 => 5, 5 => 6], + [6 => 7, 7 => 8], + [8 => 9, 9 => 10], + ], + ); + + $this->assertSame( + actual: $collection + ->chunk(3) + ->map(fn ($chunk) => $chunk->toArray()) + ->toArray(), + expected: [ + [0 => 1, 1 => 2, 2 => 3], + [3 => 4, 4 => 5, 5 => 6], + [6 => 7, 7 => 8, 8 => 9], + [9 => 10], + ], + ); + } } From a3e8230f262158ea889c1585ca30f2570daf4ba2 Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Tue, 12 Nov 2024 15:21:12 +0100 Subject: [PATCH 11/17] :sparkles: Add method search() --- src/Tempest/Support/src/ArrayHelper.php | 22 +++++ src/Tempest/Support/tests/ArrayHelperTest.php | 87 +++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index 15218a9e4..a9d4cf42e 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -10,6 +10,7 @@ use Generator; use InvalidArgumentException; use Iterator; +use PhpParser\Node\Expr\Instanceof_; use Random\Randomizer; use Serializable; use Stringable; @@ -44,6 +45,27 @@ public function __construct( } } + /** + * Search for a given value in the array and return the corresponding key if successful. + * + * @param \Closure|mixed $value The value to search for, a Closure will check the first item that returns true. + * @param boolean $strict Whether to use strict comparison. + * + * @return array-key|false The key for `$value` if found, otherwise `false`. + */ + public function search(mixed $value, bool $strict = false): int|string|false { + if ( ! $value instanceof Closure ) { + return array_search($value, $this->array, $strict); + } + + foreach ($this->array as $key => $item) { + if ($value($item, $key) === true) { + return $key; + } + } + + return false; + } /** * Chunk the array into chunks of the given size. diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index 0240faa62..dd603b9e8 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1480,4 +1480,91 @@ public function test_chunk_preserve_keys(): void { ], ); } + + public function test_search_with_simple_value(): void + { + $collection = arr(['apple', 'banana', 'orange']); + + $this->assertSame(1, $collection->search('banana')); + $this->assertSame(0, $collection->search('apple')); + $this->assertFalse($collection->search('grape')); + } + + public function test_search_with_strict_comparison(): void + { + $collection = arr([1, '1', 2, '2']); + + // Non-strict + $this->assertSame(0, $collection->search(1, strict: false)); + $this->assertSame(0, $collection->search('1', strict: false)); + + // Strict + $this->assertSame(0, $collection->search(1, strict: true)); + $this->assertSame(1, $collection->search('1', strict: true)); + } + + public function test_search_with_closure(): void + { + $collection = arr([ + ['id' => 1, 'name' => 'John'], + ['id' => 2, 'name' => 'Jane'], + ['id' => 3, 'name' => 'Bob'], + ]); + + // Recherche par condition + $result = $collection->search(fn($item) => $item['name'] === 'Jane'); + $this->assertSame(1, $result); + + // Recherche avec clé + $result = $collection->search(fn($item, $key) => $key === 2); + $this->assertSame(2, $result); + + // Recherche qui échoue + $result = $collection->search(fn($item) => $item['name'] === 'Alice'); + $this->assertFalse($result); + } + + public function test_search_with_string_keys(): void + { + $collection = arr([ + 'first' => 'value1', + 'second' => 'value2', + 'third' => 'value3', + ]); + + $this->assertSame('second', $collection->search('value2')); + $this->assertFalse($collection->search('value4')); + } + + public function test_search_with_null_values(): void + { + $collection = arr(['a', null, 'b', '']); + + $this->assertSame(1, $collection->search(null)); + $this->assertSame(1, $collection->search('')); + } + + public function test_search_with_complex_closure(): void + { + $collection = arr([ + ['age' => 25, 'active' => true], + ['age' => 30, 'active' => false], + ['age' => 35, 'active' => true], + ]); + + $result = $collection->search(function($item) { + return $item['age'] > 28 && $item['active'] === true; + }); + + $this->assertSame(2, $result); + } + + public function test_search_with_empty_array(): void + { + $collection = arr([]); + + $this->assertFalse($collection->search('anything')); + $this->assertFalse($collection->search(fn() => true)); + } + } From eeb5eaf18c05203044a4e439400f9c7f4a58a5cf Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Tue, 12 Nov 2024 15:22:48 +0100 Subject: [PATCH 12/17] :art: Coding styles --- src/Tempest/Support/src/ArrayHelper.php | 28 +++++++------- src/Tempest/Support/tests/ArrayHelperTest.php | 38 +++++++++---------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index a9d4cf42e..b937ef431 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -10,7 +10,6 @@ use Generator; use InvalidArgumentException; use Iterator; -use PhpParser\Node\Expr\Instanceof_; use Random\Randomizer; use Serializable; use Stringable; @@ -48,13 +47,14 @@ public function __construct( /** * Search for a given value in the array and return the corresponding key if successful. * - * @param \Closure|mixed $value The value to search for, a Closure will check the first item that returns true. - * @param boolean $strict Whether to use strict comparison. + * @param Closure|mixed $value The value to search for, a Closure will check the first item that returns true. + * @param bool $strict Whether to use strict comparison. * * @return array-key|false The key for `$value` if found, otherwise `false`. */ - public function search(mixed $value, bool $strict = false): int|string|false { - if ( ! $value instanceof Closure ) { + public function search(mixed $value, bool $strict = false): int|string|false + { + if (! $value instanceof Closure) { return array_search($value, $this->array, $strict); } @@ -70,19 +70,20 @@ public function search(mixed $value, bool $strict = false): int|string|false { /** * Chunk the array into chunks of the given size. * - * @param integer $size The size of each chunk. + * @param int $size The size of each chunk. * @param bool $preserveKeys Whether to preserve the keys of the original array. * * @return self */ - public function chunk(int $size, bool $preserveKeys = true): self { - if ( $size <= 0 ) { - return new self; + public function chunk(int $size, bool $preserveKeys = true): self + { + if ($size <= 0) { + return new self(); } $chunks = []; - foreach ( array_chunk($this->array, $size, $preserveKeys) as $chunk ) { + foreach (array_chunk($this->array, $size, $preserveKeys) as $chunk) { $chunks[] = new self($chunk); } @@ -94,13 +95,14 @@ public function chunk(int $size, bool $preserveKeys = true): self { * * @template TReduceInitial * @template TReduceReturnType - * + * * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback * @param TReduceInitial $initial - * + * * @return TReduceReturnType */ - public function reduce(callable $callback, mixed $initial = null): mixed { + public function reduce(callable $callback, mixed $initial = null): mixed + { $result = $initial; foreach ($this->array as $key => $value) { diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index dd603b9e8..5a2cb46b7 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1401,7 +1401,7 @@ public function test_reduce_with_existing_function(): void $collection = arr([ [1, 2, 2, 3], [2, 3, 3, 4], - [3, 1, 3, 1] + [3, 1, 3, 1], ]); $this->assertSame( @@ -1450,7 +1450,8 @@ public function test_chunk(): void ); } - public function test_chunk_preserve_keys(): void { + public function test_chunk_preserve_keys(): void + { $collection = arr([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); $this->assertSame( @@ -1484,7 +1485,7 @@ public function test_chunk_preserve_keys(): void { public function test_search_with_simple_value(): void { $collection = arr(['apple', 'banana', 'orange']); - + $this->assertSame(1, $collection->search('banana')); $this->assertSame(0, $collection->search('apple')); $this->assertFalse($collection->search('grape')); @@ -1493,11 +1494,11 @@ public function test_search_with_simple_value(): void public function test_search_with_strict_comparison(): void { $collection = arr([1, '1', 2, '2']); - + // Non-strict $this->assertSame(0, $collection->search(1, strict: false)); $this->assertSame(0, $collection->search('1', strict: false)); - + // Strict $this->assertSame(0, $collection->search(1, strict: true)); $this->assertSame(1, $collection->search('1', strict: true)); @@ -1510,17 +1511,17 @@ public function test_search_with_closure(): void ['id' => 2, 'name' => 'Jane'], ['id' => 3, 'name' => 'Bob'], ]); - + // Recherche par condition - $result = $collection->search(fn($item) => $item['name'] === 'Jane'); + $result = $collection->search(fn ($item) => $item['name'] === 'Jane'); $this->assertSame(1, $result); - + // Recherche avec clé - $result = $collection->search(fn($item, $key) => $key === 2); + $result = $collection->search(fn ($item, $key) => $key === 2); $this->assertSame(2, $result); - + // Recherche qui échoue - $result = $collection->search(fn($item) => $item['name'] === 'Alice'); + $result = $collection->search(fn ($item) => $item['name'] === 'Alice'); $this->assertFalse($result); } @@ -1531,7 +1532,7 @@ public function test_search_with_string_keys(): void 'second' => 'value2', 'third' => 'value3', ]); - + $this->assertSame('second', $collection->search('value2')); $this->assertFalse($collection->search('value4')); } @@ -1539,7 +1540,7 @@ public function test_search_with_string_keys(): void public function test_search_with_null_values(): void { $collection = arr(['a', null, 'b', '']); - + $this->assertSame(1, $collection->search(null)); $this->assertSame(1, $collection->search('')); } @@ -1551,20 +1552,19 @@ public function test_search_with_complex_closure(): void ['age' => 30, 'active' => false], ['age' => 35, 'active' => true], ]); - - $result = $collection->search(function($item) { + + $result = $collection->search(function ($item) { return $item['age'] > 28 && $item['active'] === true; }); - + $this->assertSame(2, $result); } public function test_search_with_empty_array(): void { $collection = arr([]); - + $this->assertFalse($collection->search('anything')); - $this->assertFalse($collection->search(fn() => true)); + $this->assertFalse($collection->search(fn () => true)); } - } From 278da3db716e902f2a817e9daa7b665299e240a3 Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Tue, 12 Nov 2024 15:56:47 +0100 Subject: [PATCH 13/17] :rotating_light: Fix phpstan --- src/Tempest/Support/src/ArrayHelper.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index b937ef431..4c25d8222 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -73,7 +73,7 @@ public function search(mixed $value, bool $strict = false): int|string|false * @param int $size The size of each chunk. * @param bool $preserveKeys Whether to preserve the keys of the original array. * - * @return self + * @return self */ public function chunk(int $size, bool $preserveKeys = true): self { @@ -82,7 +82,6 @@ public function chunk(int $size, bool $preserveKeys = true): self } $chunks = []; - foreach (array_chunk($this->array, $size, $preserveKeys) as $chunk) { $chunks[] = new self($chunk); } From 16244e865f25b2d5a14af21998f0347a7ed221d9 Mon Sep 17 00:00:00 2001 From: Gturpin <34162504+gturpin-dev@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:14:05 +0100 Subject: [PATCH 14/17] :pencil: Fix PHPDoc Co-authored-by: Enzo Innocenzi --- src/Tempest/Support/src/ArrayHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index 4c25d8222..526f824eb 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -68,7 +68,7 @@ public function search(mixed $value, bool $strict = false): int|string|false } /** - * Chunk the array into chunks of the given size. + * Chunks the array into chunks of the given size. * * @param int $size The size of each chunk. * @param bool $preserveKeys Whether to preserve the keys of the original array. From 469abb971ee0c30dbea232a1d95525b2ef4ec21d Mon Sep 17 00:00:00 2001 From: Gturpin <34162504+gturpin-dev@users.noreply.github.com> Date: Tue, 12 Nov 2024 16:14:20 +0100 Subject: [PATCH 15/17] :pencil: Fix PHPDoc Co-authored-by: Enzo Innocenzi --- src/Tempest/Support/src/ArrayHelper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index 526f824eb..5cd11eb8b 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -90,7 +90,7 @@ public function chunk(int $size, bool $preserveKeys = true): self } /** - * Reduce the array to a single value using a callback. + * Reduces the array to a single value using a callback. * * @template TReduceInitial * @template TReduceReturnType From 3eba268a7917f4f568881fb3a95eaf54187831e2 Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Wed, 13 Nov 2024 09:55:44 +0100 Subject: [PATCH 16/17] :fire: Remove french comments --- src/Tempest/Support/tests/ArrayHelperTest.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index 5a2cb46b7..707836117 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1495,11 +1495,9 @@ public function test_search_with_strict_comparison(): void { $collection = arr([1, '1', 2, '2']); - // Non-strict $this->assertSame(0, $collection->search(1, strict: false)); $this->assertSame(0, $collection->search('1', strict: false)); - // Strict $this->assertSame(0, $collection->search(1, strict: true)); $this->assertSame(1, $collection->search('1', strict: true)); } @@ -1512,15 +1510,12 @@ public function test_search_with_closure(): void ['id' => 3, 'name' => 'Bob'], ]); - // Recherche par condition $result = $collection->search(fn ($item) => $item['name'] === 'Jane'); $this->assertSame(1, $result); - // Recherche avec clé $result = $collection->search(fn ($item, $key) => $key === 2); $this->assertSame(2, $result); - // Recherche qui échoue $result = $collection->search(fn ($item) => $item['name'] === 'Alice'); $this->assertFalse($result); } From fd71d3c6f9aecc45a601dfd96e6127aee729dce4 Mon Sep 17 00:00:00 2001 From: Guillaume Turpin Date: Wed, 13 Nov 2024 10:32:37 +0100 Subject: [PATCH 17/17] :sparkles: Update the search method to findKey --- src/Tempest/Support/src/ArrayHelper.php | 14 +++--- src/Tempest/Support/tests/ArrayHelperTest.php | 50 +++++++++---------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/Tempest/Support/src/ArrayHelper.php b/src/Tempest/Support/src/ArrayHelper.php index 5cd11eb8b..f1465d030 100644 --- a/src/Tempest/Support/src/ArrayHelper.php +++ b/src/Tempest/Support/src/ArrayHelper.php @@ -45,17 +45,19 @@ public function __construct( } /** - * Search for a given value in the array and return the corresponding key if successful. + * Finds a value in the array and return the corresponding key if successful. * - * @param Closure|mixed $value The value to search for, a Closure will check the first item that returns true. + * @param (Closure(TValue, TKey): bool)|mixed $value The value to search for, a Closure will find the first item that returns true. * @param bool $strict Whether to use strict comparison. * - * @return array-key|false The key for `$value` if found, otherwise `false`. + * @return array-key|null The key for `$value` if found, `null` otherwise. */ - public function search(mixed $value, bool $strict = false): int|string|false + public function findKey(mixed $value, bool $strict = false): int|string|null { if (! $value instanceof Closure) { - return array_search($value, $this->array, $strict); + $search = array_search($value, $this->array, $strict); + + return $search === false ? null : $search; // Keep empty values but convert false to null } foreach ($this->array as $key => $item) { @@ -64,7 +66,7 @@ public function search(mixed $value, bool $strict = false): int|string|false } } - return false; + return null; } /** diff --git a/src/Tempest/Support/tests/ArrayHelperTest.php b/src/Tempest/Support/tests/ArrayHelperTest.php index 707836117..91f3246b2 100644 --- a/src/Tempest/Support/tests/ArrayHelperTest.php +++ b/src/Tempest/Support/tests/ArrayHelperTest.php @@ -1482,27 +1482,27 @@ public function test_chunk_preserve_keys(): void ); } - public function test_search_with_simple_value(): void + public function test_find_key_with_simple_value(): void { $collection = arr(['apple', 'banana', 'orange']); - $this->assertSame(1, $collection->search('banana')); - $this->assertSame(0, $collection->search('apple')); - $this->assertFalse($collection->search('grape')); + $this->assertSame(1, $collection->findKey('banana')); + $this->assertSame(0, $collection->findKey('apple')); + $this->assertNull($collection->findKey('grape')); } - public function test_search_with_strict_comparison(): void + public function test_find_key_with_strict_comparison(): void { $collection = arr([1, '1', 2, '2']); - $this->assertSame(0, $collection->search(1, strict: false)); - $this->assertSame(0, $collection->search('1', strict: false)); + $this->assertSame(0, $collection->findKey(1, strict: false)); + $this->assertSame(0, $collection->findKey('1', strict: false)); - $this->assertSame(0, $collection->search(1, strict: true)); - $this->assertSame(1, $collection->search('1', strict: true)); + $this->assertSame(0, $collection->findKey(1, strict: true)); + $this->assertSame(1, $collection->findKey('1', strict: true)); } - public function test_search_with_closure(): void + public function test_find_key_with_closure(): void { $collection = arr([ ['id' => 1, 'name' => 'John'], @@ -1510,17 +1510,17 @@ public function test_search_with_closure(): void ['id' => 3, 'name' => 'Bob'], ]); - $result = $collection->search(fn ($item) => $item['name'] === 'Jane'); + $result = $collection->findKey(fn ($item) => $item['name'] === 'Jane'); $this->assertSame(1, $result); - $result = $collection->search(fn ($item, $key) => $key === 2); + $result = $collection->findKey(fn ($item, $key) => $key === 2); $this->assertSame(2, $result); - $result = $collection->search(fn ($item) => $item['name'] === 'Alice'); - $this->assertFalse($result); + $result = $collection->findKey(fn ($item) => $item['name'] === 'Alice'); + $this->assertNull($result); } - public function test_search_with_string_keys(): void + public function test_find_key_with_string_keys(): void { $collection = arr([ 'first' => 'value1', @@ -1528,19 +1528,19 @@ public function test_search_with_string_keys(): void 'third' => 'value3', ]); - $this->assertSame('second', $collection->search('value2')); - $this->assertFalse($collection->search('value4')); + $this->assertSame('second', $collection->findKey('value2')); + $this->assertNull($collection->findKey('value4')); } - public function test_search_with_null_values(): void + public function test_find_key_with_null_values(): void { $collection = arr(['a', null, 'b', '']); - $this->assertSame(1, $collection->search(null)); - $this->assertSame(1, $collection->search('')); + $this->assertSame(1, $collection->findKey(null)); + $this->assertSame(1, $collection->findKey('')); } - public function test_search_with_complex_closure(): void + public function test_find_key_with_complex_closure(): void { $collection = arr([ ['age' => 25, 'active' => true], @@ -1548,18 +1548,18 @@ public function test_search_with_complex_closure(): void ['age' => 35, 'active' => true], ]); - $result = $collection->search(function ($item) { + $result = $collection->findKey(function ($item) { return $item['age'] > 28 && $item['active'] === true; }); $this->assertSame(2, $result); } - public function test_search_with_empty_array(): void + public function test_find_key_with_empty_array(): void { $collection = arr([]); - $this->assertFalse($collection->search('anything')); - $this->assertFalse($collection->search(fn () => true)); + $this->assertNull($collection->findKey('anything')); + $this->assertNull($collection->findKey(fn () => true)); } }