Skip to content

Commit

Permalink
feat - add the has_shape method
Browse files Browse the repository at this point in the history
  • Loading branch information
lucatume committed Mar 15, 2024
1 parent fe0a4ce commit cfa0ed9
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 10 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ A library for array manipulations.
* [get_first_set](/docs/classes/StellarWP/Arrays/Arr.md#get_first_set)
* [get_in_any](/docs/classes/StellarWP/Arrays/Arr.md#get_in_any)
* [has](/docs/classes/StellarWP/Arrays/Arr.md#has)
* [has_shape]/docs/classes/StellarWP/Arrays/Arr.md#has_shape)
* [insert_after_key](/docs/classes/StellarWP/Arrays/Arr.md#insert_after_key)
* [insert_before_key](/docs/classes/StellarWP/Arrays/Arr.md#insert_before_key)
* [is_assoc](/docs/classes/StellarWP/Arrays/Arr.md#is_assoc)
Expand Down
24 changes: 21 additions & 3 deletions docs/classes/StellarWP/Arrays/Arr.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ The sanitized array

**See Also:**

* https://gist.github.com/esthezia/5804445 -
* https://gist.github.com/esthezia/5804445 -

***

Expand Down Expand Up @@ -604,7 +604,25 @@ public static has(\ArrayAccess|array $array, array|string|int|null $indexes): bo
| `$indexes` | **array|string|int|null** | The indexes to search; in order the function will look from the first to the last. |


### has_shape

Check if an array has a specific shape.

```php
public static has_shape(mixed $array, array $shape): bool
```

* This method is **static**.




**Parameters:**

| Parameter | Type | Description |
|-----------|-----------|-----------------------------------------------------------------------------------|
| `$array` | **mixed** | The array to check. |
| `$shape` | **array** | The shape to check for. A map from keys to the callable or Closure to check them. |

***

Expand Down Expand Up @@ -865,7 +883,7 @@ public merge_recursive(array& $array1, array& $array2): array

**See Also:**

* http://php.net/manual/en/function.array-merge-recursive.php#92195 -
* http://php.net/manual/en/function.array-merge-recursive.php#92195 -

***

Expand Down Expand Up @@ -1415,7 +1433,7 @@ Integer position of first needle occurrence.

**See Also:**

* \StellarWP\Arrays\strpos() -
* \StellarWP\Arrays\strpos() -

***

Expand Down
60 changes: 56 additions & 4 deletions src/Arrays/Arr.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
namespace StellarWP\Arrays;

use ArrayAccess;
use BadMethodCallException;
use Illuminate\Support\Enumerable;
use InvalidArgumentException;
use Throwable;

/**
* Array utilities
Expand Down Expand Up @@ -299,8 +301,8 @@ public static function except( $array, $keys ) {
/**
* Determine if the given key exists in the provided array.
*
* @param \ArrayAccess|array $array
* @param string|int|float $key
* @param ArrayAccess|array $array
* @param string|int|float $key
*
* @return bool
*/
Expand Down Expand Up @@ -543,7 +545,7 @@ public static function get_in_any( array $variables, $indexes, $default = null )
/**
* Check if an item or items exist in an array using "dot" notation.
*
* @param \ArrayAccess|array $array
* @param ArrayAccess|array $array
* @param array|string|int|null $indexes The indexes to search; in order the function will look from the first to the last.
*
* @return bool
Expand Down Expand Up @@ -942,7 +944,7 @@ public static function query( $array ) {
*
* @return mixed
*
* @throws \InvalidArgumentException
* @throws InvalidArgumentException
*/
public static function random( $array, $number = null, $preserveKeys = false ) {
$requested = is_null( $number ) ? 1 : $number;
Expand Down Expand Up @@ -1386,4 +1388,54 @@ public static function wrap( $value ) {

return is_array( $value ) ? $value : [ $value ];
}

/**
* Checks if an array has a specific shape.
*
* @since TBD
*
* @param array $array The array to check.
* @param array<string|int,callable> $shape The shape to check for. Each key, either a string or an integer,
* maps to a callable that will be used to validate the value at that key.
* The callable must have the signature `fn( mixed $value ) :bool`.
* @param bool $strict Whether the array should only contain the keys specified in the shape.
*
* @return bool Whether the array has the specified shape.
*/
public static function has_shape( $array, array $shape, bool $strict = false ): bool {
if ( ! is_array( $array ) ) {
return false;
}

if (
$strict
&& (
array_intersect_key( $array, $shape ) !== $array
||
array_diff_key( $array, $shape ) !== []
)
) {
return false;
}

if ( count( array_intersect_key( $shape, $array ) ) < count( $shape ) ) {
return false;
}

foreach ( $shape as $key => $check ) {
if ( ! is_callable( $check ) ) {
throw new \BadMethodCallException( 'The shape array must contain only callables as values.' );
}

try {
if ( ! $check( $array[ $key ] ) ) {
return false;
}
} catch ( \Throwable $th ) {
return false;
}
}

return true;
}
}
85 changes: 82 additions & 3 deletions tests/wpunit/ArraysTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -766,10 +766,89 @@ public function array_visit_recursive_data_provider() {
];
}

public function has_shape_data_provider(): array {
return [
'not an array' => [ 'foo', [], true, false ],
'empty array, empty shape' => [ [], [], true, true ],
'empty array, non-empty shape, strict' => [
[],
[ 'foo' => 'is_string' ],
true,
false
],
'empty array, non-empty shape, non-strict' => [
[],
[ 'foo' => 'is_string' ],
false,
false
],
'non-empty array, function shape, missing key, strict' => [
[ 'foo' => 23 ],
[ 'bar' => 'is_string' ],
true,
false
],
'non-empty array, function shape, missing key, non-strict' => [
[ 'foo' => 23 ],
[ 'bar' => 'is_string' ],
false,
false
],
'non-empty array, function shape, extra key, strict' => [
[ 'foo' => 23, 'bar' => 'baz' ],
[ 'foo' => 'is_int' ],
true,
false
],
'non-empty array, function shape, extra key, non-strict' => [
[ 'foo' => 23, 'bar' => 'baz' ],
[ 'foo' => 'is_int' ],
false,
true
],
'non-empty array, closure shape, all key fail failure, strict' => [
[ 'foo' => 23, 'bar' => 89 ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
true,
false
],
'non-empty array, closure shape, all key fail failure, non-strict' => [
[ 'foo' => 23, 'bar' => 89 ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
false,
false
],
'non-empty array, closure shape, all key pass, strict' => [
[ 'foo' => 'hello', 'bar' => 'world' ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
true,
true
],
'non-empty array, closure shape, all key pass, non-strict ' => [
[ 'foo' => 'hello', 'bar' => 'world' ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
false,
true
],
'non-empty array, closure shape, some key pass, strict' => [
[ 'foo' => 'hello', 'bar' => 89 ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
true,
false
],
'non-empty array, closure shape, some key pass, non-strict' => [
[ 'foo' => 'hello', 'bar' => 89 ],
[ 'foo' => fn( $foo ) => $foo === 'hello', 'bar' => fn( $bar ) => $bar === 'world' ],
false,
false
],
];
}

/**
* @dataProvider array_visit_recursive_data_provider
* @dataProvider has_shape_data_provider
*/
public function test_array_visit_recursive( $input, $visitor, $expected ) {
$this->assertEqualSets( $expected, Arr::array_visit_recursive( $input, $visitor ) );
public function test_has_shape( $input, $shape, $strict, $expected ): void {
$this->assertEquals( $expected, Arr::has_shape( $input, $shape, $strict ) );
}
}

0 comments on commit cfa0ed9

Please sign in to comment.