Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement value retrieval and checks by predicate callback #142

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Yii Arrays Change Log

## 3.0.1 under development
## 4.0.0 under development

- Chg #142: Redefine the callable parameter in `ArrayHelper::getValue()` to be a value matcher (yus-ham)
- New #139: Add `ArrayHelper::parametrizedMerge()` method that allows to merge two or more arrays recursively with
specified depth (@vjik)
- Enh #140: Remove `null` from return type of `ArrayHelper::getObjectVars()` method (@Tigrov)
Expand Down
48 changes: 31 additions & 17 deletions src/ArrayHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
if (is_int($key)) {
$result[$name] = $object->$name;
} else {
$result[$key] = self::getValue($object, $name);
$result[$key] = $name instanceof Closure ? $name($object) : self::getValue($object, $name);

Check failure on line 101 in src/ArrayHelper.php

View workflow job for this annotation

GitHub Actions / psalm80 / PHP 8.0-ubuntu-latest

DocblockTypeContradiction

src/ArrayHelper.php:101:45: DocblockTypeContradiction: Cannot resolve types for $name - docblock-defined type string does not contain Closure (see https://psalm.dev/155)

Check failure on line 101 in src/ArrayHelper.php

View workflow job for this annotation

GitHub Actions / psalm / PHP 8.1-ubuntu-latest

DocblockTypeContradiction

src/ArrayHelper.php:101:45: DocblockTypeContradiction: Cannot resolve types for $name - docblock-defined type string does not contain Closure (see https://psalm.dev/155)

Check failure on line 101 in src/ArrayHelper.php

View workflow job for this annotation

GitHub Actions / psalm / PHP 8.2-ubuntu-latest

DocblockTypeContradiction

src/ArrayHelper.php:101:45: DocblockTypeContradiction: Cannot resolve types for $name - docblock-defined type string does not contain Closure (see https://psalm.dev/155)

Check failure on line 101 in src/ArrayHelper.php

View workflow job for this annotation

GitHub Actions / psalm / PHP 8.3-ubuntu-latest

DocblockTypeContradiction

src/ArrayHelper.php:101:45: DocblockTypeContradiction: Cannot resolve types for $name - docblock-defined type string does not contain Closure (see https://psalm.dev/155)
}
}

Expand Down Expand Up @@ -167,9 +167,9 @@
* // Working with object:
* $username = \Yiisoft\Arrays\ArrayHelper::getValue($user, 'username');
*
* // Working with anonymous function:
* $fullName = \Yiisoft\Arrays\ArrayHelper::getValue($user, function ($user, $defaultValue) {
* return $user->firstName . ' ' . $user->lastName;
* // Retrieve the value by matcher function:
* $firstActiveMember = \Yiisoft\Arrays\ArrayHelper::getValue($users, function ($user, $key) {
* return $user->type === 'member' && $user->isActive;
* });
*
* // Using an array of keys to retrieve the value:
Expand All @@ -178,23 +178,22 @@
*
* @param array|object $array Array or object to extract value from.
* @param array|Closure|float|int|string $key Key name of the array element,
* an array of keys, object property name, object method like `getName()`, or an anonymous function
* returning the value. The anonymous function signature should be:
* `function($array, $defaultValue)`.
* an array of keys, object property name, object method like `getName()` or a callable. The callable function signature should be:
* `function($value, $key)`.
* @param mixed $default The default value to be returned if the specified array key does not exist. Not used when
* getting value from an object.
*
* @psalm-param ArrayKey|Closure $key
*
* @return mixed The value of the element if found, default value otherwise.
* @return mixed The value of the element if found or the return value of callable is truthy, default value otherwise.
*/
public static function getValue(
array|object $array,
array|Closure|float|int|string $key,
mixed $default = null
): mixed {
if ($key instanceof Closure) {
return $key($array, $default);
return self::getValueByMatcher($array, $key, $default);

Check failure on line 196 in src/ArrayHelper.php

View workflow job for this annotation

GitHub Actions / psalm80 / PHP 8.0-ubuntu-latest

PossiblyInvalidArgument

src/ArrayHelper.php:196:44: PossiblyInvalidArgument: Argument 1 of Yiisoft\Arrays\ArrayHelper::getValueByMatcher expects array<array-key, mixed>, but possibly different type array<array-key, mixed>|object provided (see https://psalm.dev/092)

Check failure on line 196 in src/ArrayHelper.php

View workflow job for this annotation

GitHub Actions / psalm / PHP 8.1-ubuntu-latest

PossiblyInvalidArgument

src/ArrayHelper.php:196:44: PossiblyInvalidArgument: Argument 1 of Yiisoft\Arrays\ArrayHelper::getValueByMatcher expects array<array-key, mixed>, but possibly different type array<array-key, mixed>|object provided (see https://psalm.dev/092)

Check failure on line 196 in src/ArrayHelper.php

View workflow job for this annotation

GitHub Actions / psalm / PHP 8.2-ubuntu-latest

PossiblyInvalidArgument

src/ArrayHelper.php:196:44: PossiblyInvalidArgument: Argument 1 of Yiisoft\Arrays\ArrayHelper::getValueByMatcher expects array<array-key, mixed>, but possibly different type array<array-key, mixed>|object provided (see https://psalm.dev/092)

Check failure on line 196 in src/ArrayHelper.php

View workflow job for this annotation

GitHub Actions / psalm / PHP 8.3-ubuntu-latest

PossiblyInvalidArgument

src/ArrayHelper.php:196:44: PossiblyInvalidArgument: Argument 1 of Yiisoft\Arrays\ArrayHelper::getValueByMatcher expects array<array-key, mixed>, but possibly different type array<array-key, mixed>|object provided (see https://psalm.dev/092)
}

if (is_array($key)) {
Expand Down Expand Up @@ -253,6 +252,21 @@
return $default;
}

private static function getValueByMatcher(
array $array,
Closure $match,
mixed $default = null
): mixed
{
foreach ($array as $key => $value) {
if ($match($value, $key)) {
return $value;
}
}

return $default;
}

/**
* Retrieves the value of an array element or object property with the given key or property name.
* If the key does not exist in the array or object, the default value will be returned instead.
Expand Down Expand Up @@ -761,7 +775,7 @@
$lastArray[] = $element;
}
} else {
$value = self::getValue($element, $key);
$value = $key instanceof Closure ? $key($element) : self::getValue($element, $key);
if ($value !== null) {
$lastArray[self::normalizeArrayKey($value)] = $element;
}
Expand Down Expand Up @@ -823,11 +837,11 @@
$result = [];
if ($keepKeys) {
foreach ($array as $k => $element) {
$result[$k] = self::getValue($element, $name);
$result[$k] = $name instanceof Closure ? $name($element) : self::getValue($element, $name);
}
} else {
foreach ($array as $element) {
$result[] = self::getValue($element, $name);
$result[] = $name instanceof Closure ? $name($element) : self::getValue($element, $name);
}
}

Expand Down Expand Up @@ -888,8 +902,8 @@
if ($from instanceof Closure || $to instanceof Closure || !is_array($array)) {
$result = [];
foreach ($array as $element) {
$key = (string)self::getValue($element, $from);
$result[$key] = self::getValue($element, $to);
$key = (string)($from instanceof Closure ? $from($element) : self::getValue($element, $from));
$result[$key] = $to instanceof Closure ? $to($element) : self::getValue($element, $to);
}

return $result;
Expand All @@ -900,9 +914,9 @@

$result = [];
foreach ($array as $element) {
$groupKey = (string)self::getValue($element, $group);
$key = (string)self::getValue($element, $from);
$result[$groupKey][$key] = self::getValue($element, $to);
$groupKey = (string)($group instanceof Closure ? $group($element) : self::getValue($element, $group));
$key = (string)($from instanceof Closure ? $from($element) : self::getValue($element, $from));
$result[$groupKey][$key] = $to instanceof Closure ? $to($element) : self::getValue($element, $to);
}

return $result;
Expand Down
25 changes: 20 additions & 5 deletions tests/ArrayHelper/GetValueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,6 @@ private function commonDataProviderFromArray(): array
['name', 'test'],
['noname', null],
['noname', 'test', 'test'],
[
yus-ham marked this conversation as resolved.
Show resolved Hide resolved
static fn ($array, $defaultValue) => $array['date'] . $defaultValue,
'31-12-2113test',
'test',
],
[['version', 2], 'two'],
[['version', 2.0], 'two'],
[42.7, 500],
Expand Down Expand Up @@ -89,6 +84,26 @@ public function testGetValueFromArray($key, $expected, $default = null): void
$this->assertEquals($expected, ArrayHelper::getValue($this->array, $key, $default));
}

public function testGetValueByMatcher(): void
{
$users = [
['name' => 'Cebe', 'status' => 'active'],
['name' => 'John', 'status' => 'not active'],
];

$activeUser = ArrayHelper::getValue($users, fn ($user) => $user['status'] === 'active');
$this->assertEquals('Cebe', $activeUser['name']);


$posts = [
(object)['title' => 'hello world'],
(object)['title' => 'hello test', 'tag' => 'test'],
];

$taggedPost = ArrayHelper::getValue($posts, fn ($post) => isset($post->tag));
$this->assertEquals('hello test', $taggedPost->title);
}

public function dataProviderGetValueByPathFromArray(): array
{
return array_merge($this->commonDataProviderFromArray(), [
Expand Down
Loading