Skip to content

Commit d86a053

Browse files
committed
Added find method
1 parent 28cd7f0 commit d86a053

File tree

5 files changed

+113
-4
lines changed

5 files changed

+113
-4
lines changed

README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,15 @@ Advanced implementation of well known operations:
3434
## Example of usage
3535
```php
3636
// Set nested array value
37-
$array = Arr::set([], 'key1.key2.key3', 'my_value');
37+
$array = Arr::set([], 'key1.key2.key3', 'my_value');
38+
// Which is equivalent to
39+
[
40+
'key1' => [
41+
'key2' => [
42+
'key3' => 'my_value'
43+
]
44+
]
45+
]
3846

3947
// Get nested array value
4048
Arr::get($array, 'key1.key2') -> ['key3' => 'my_value']
@@ -47,6 +55,11 @@ Arr::map($array, function ($key, $value) {
4755
// Your code here
4856
});
4957

58+
// Find array element
59+
Arr::find($array, function ($element) {
60+
return Arr::get($element, 'key2.key3') === 'my_value';
61+
}) -> [ 'key2' => [ 'key3' => 'my_value'] ]
62+
5063
// Chain few methods
5164
Arr::obj(['test' => 1, 'foo' => 'bar'])
5265
->set('abc', 123)

src/Arr.php

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
use ArrayAccess;
1212
use InvalidArgumentException;
13+
use Iterator;
14+
use IteratorAggregate;
1315
use ReflectionFunction;
1416
use ReflectionMethod;
1517
use Throwable;
@@ -59,6 +61,19 @@ class Arr
5961
*/
6062
const UNPACK_PRESERVE_ARRAY = 8;
6163

64+
/**
65+
* Return value of array element matching find condition
66+
*/
67+
const FIND_RETURN_VALUE = 'value';
68+
/**
69+
* Return key of array element matching find condition
70+
*/
71+
const FIND_RETURN_KEY = 'key';
72+
/**
73+
* Return all values (preserving original keys) of array elements matching find condition
74+
*/
75+
const FIND_RETURN_ALL = 'all';
76+
6277
private const AUTO_INDEX_KEY = '[]';
6378
private const KEY_SEPARATOR = '.';
6479

@@ -112,7 +127,7 @@ public static function has(array $array, $keys): bool
112127

113128
foreach ($keysArray as $key) {
114129
if (!array_key_exists($key, $tmp)) {
115-
return false;
130+
return false;
116131
}
117132
$tmp = $tmp[$key];
118133
}
@@ -692,6 +707,34 @@ public static function groupObjects(array $objects, string $method, ...$args): a
692707
return $return;
693708
}
694709

710+
/**
711+
* Find array (or iterable object) element(s) that match specified condition
712+
*
713+
* @param array|Iterator|IteratorAggregate $array
714+
* @param callable $condition Callable accepting one argument (current array element value) and returning truthy or falsy value
715+
* @param string $return What type of result should be returned after finding desired element(s)
716+
* @return mixed|mixed[] Either key, value or assoc array containing keys and values for elements matching specified condition. Returns null if element was not found, or empty array if FIND_RETURN_ALL mode was used.
717+
*/
718+
public static function find($array, callable $condition, string $return = self::FIND_RETURN_VALUE)
719+
{
720+
$result = [];
721+
foreach ($array as $key => $value) {
722+
if (call_user_func($condition, $value)) {
723+
switch ($return) {
724+
case self::FIND_RETURN_KEY:
725+
return $key;
726+
case self::FIND_RETURN_VALUE:
727+
return $value;
728+
case self::FIND_RETURN_ALL:
729+
$result[$key] = $value;
730+
break;
731+
}
732+
}
733+
}
734+
735+
return $return === self::FIND_RETURN_ALL ? $result : null;
736+
}
737+
695738
/**
696739
* Order associative array according to supplied keys order
697740
* Keys that are not present in $keys param will be appended to the end of an array preserving supplied order.
@@ -977,7 +1020,7 @@ public static function getDepth(array $array): int
9771020
}
9781021
}
9791022
}
980-
} while(!empty($queue));
1023+
} while (!empty($queue));
9811024

9821025
return $depth;
9831026
}

src/ArrObj.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use ArrayIterator;
77
use BadMethodCallException;
88
use Countable;
9+
use Iterator;
910
use IteratorAggregate;
1011

1112
/**
@@ -78,6 +79,11 @@
7879
*
7980
* ---------------------------------------------------------------------------------
8081
*
82+
* @method ArrObj find(array|IteratorAggregate|Iterator $array, callable $condition, string $return)
83+
* @see Arr::find()
84+
*
85+
* ---------------------------------------------------------------------------------
86+
*
8187
* @method ArrObj orderByKeys(mixed $keys, bool $appendUnmatched = true)
8288
* @see Arr::orderByKeys()
8389
*
@@ -166,6 +172,7 @@ class ArrObj implements IteratorAggregate, ArrayAccess, Countable
166172
'filterObjects',
167173
'group',
168174
'groupObjects',
175+
'find',
169176
'orderByKeys',
170177
'sortByKeys',
171178
'sortObjects',

test/Arr/ArrTest.php

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ public function testGetNestedElement($class)
9090
$this->assertSame(['test'], $this->callMethod([$class, 'get'], $array, 'key1.key2.key3'));
9191
$this->assertSame('test', $this->callMethod([$class, 'get'], $array, 'key1.key2.key3.0'));
9292
$this->assertSame('default', $this->callMethod([$class, 'get'], $array, 'key1.key4.key2.key3', 'default'));
93-
/** @noinspection PhpParamsInspection */
9493
$this->assertSame('default', $this->callMethod([$class, 'get'], new stdClass(), 'key1.key4.key2.key3', 'default'));
9594
$this->assertNull($this->callMethod([$class, 'get'], $array, 'key1.key4.key2.key3'));
9695

@@ -823,6 +822,52 @@ public function testGroupObjects($class)
823822
], $this->callMethod([$class, 'groupObjects'], ['a', 'b', 'c', $object3], 'test'));
824823
}
825824

825+
/**
826+
* @param array $array
827+
* @dataProvider arrayProvider
828+
*/
829+
public function testFind($array)
830+
{
831+
$elCount = count($array);
832+
$randomElement = Arr::random($array);
833+
$randomElementKey = array_search($randomElement, $array, true);
834+
$randomElements = $elCount === 1 ? [$randomElementKey => $randomElement] : Arr::random($array, min(3, $elCount));
835+
$callbackSingle = function ($value) use ($randomElement) {
836+
return $value === $randomElement;
837+
};
838+
$callbackAll = function ($value) use ($randomElements) {
839+
return in_array($value, $randomElements, true);
840+
};
841+
$callbackNotFound = function () {
842+
return false;
843+
};
844+
845+
// Default (value)
846+
$this->assertSame($randomElement, Arr::find($array, $callbackSingle));
847+
848+
// Return value
849+
$this->assertSame($randomElement, Arr::find($array, $callbackSingle, Arr::FIND_RETURN_VALUE));
850+
851+
// Return key
852+
$this->assertSame($randomElementKey, Arr::find($array, $callbackSingle, Arr::FIND_RETURN_KEY));
853+
854+
// Return all
855+
$this->assertSame($randomElements, Arr::find($array, $callbackAll, Arr::FIND_RETURN_ALL), "Elements " . print_r($randomElements, true));
856+
857+
// Not found (default - value)
858+
$this->assertSame(null, Arr::find($array, $callbackNotFound));
859+
860+
// Not found (value)
861+
$this->assertSame(null, Arr::find($array, $callbackNotFound, Arr::FIND_RETURN_VALUE));
862+
863+
// Not found (key)
864+
$this->assertSame(null, Arr::find($array, $callbackNotFound, Arr::FIND_RETURN_KEY));
865+
866+
// Not found (all)
867+
$this->assertSame([], Arr::find($array, $callbackNotFound, Arr::FIND_RETURN_ALL));
868+
869+
}
870+
826871
/**
827872
* @param $class string|ArrObj
828873
*

test/ArrTestCase.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ class ArrTestCase extends TestCase
1616
'bool' => true,
1717
'float' => 1.0,
1818
'null' => null,
19+
'callable' => 'boolval',
1920
];
2021

2122
/**

0 commit comments

Comments
 (0)