Elegant way to search an PHP array using a user-defined function
Solution 1:
To pull the first one from the array, or return false
:
current(array_filter($myArray, function($element) { ... }))
More info on current() here.
Solution 2:
Here's a basic solution
function array_find($xs, $f) {
foreach ($xs as $x) {
if (call_user_func($f, $x) === true)
return $x;
}
return null;
}
array_find([1,2,3,4,5,6], function($x) { return $x > 4; }); // 5
array_find([1,2,3,4,5,6], function($x) { return $x > 10; }); // null
In the event $f($x)
returns true
, the loop short circuits and $x
is immediately returned. Compared to array_filter
, this is better for our use case because array_find
does not have to continue iterating after the first positive match has been found.
In the event the callback never returns true, a value of null
is returned.
Note, I used call_user_func($f, $x)
instead of just calling $f($x)
. This is appropriate here because it allows you to use any compatible callable
Class Foo {
static private $data = 'z';
static public function match($x) {
return $x === self::$data;
}
}
array_find(['x', 'y', 'z', 1, 2, 3], ['Foo', 'match']); // 'z'
Of course it works for more complex data structures too
$data = [
(object) ['id' => 1, 'value' => 'x'],
(object) ['id' => 2, 'value' => 'y'],
(object) ['id' => 3, 'value' => 'z']
];
array_find($data, function($x) { return $x->id === 3; });
// stdClass Object (
// [id] => 3
// [value] => z
// )
If you're using PHP 7, add some type hints
function array_find(array $xs, callable $f) { ...
Solution 3:
The original array_search
returns the key of the matched value, and not the value itself (this might be useful if you're will to change the original array later).
try this function (it also works will associatives arrays)
function array_search_func(array $arr, $func)
{
foreach ($arr as $key => $v)
if ($func($v))
return $key;
return false;
}
Solution 4:
Use \iter\search()
from nikic's iter library of primitive iteration functions. It has the added benefit that it operates on both arrays and Traversable
collections.
$foundItem = \iter\search(function ($item) {
return $item > 10;
}, range(1, 20));
if ($foundItem !== null) {
echo $foundItem; // 11
}