Basically, I want to be able to get the functionality of C++'s find_if()
, Smalltalk's detect:
etc.:
// would return the element or null
check_in_array($myArray, function($element) { return $elemnt->foo() > 10; });
But I don't know of any PHP function which does this. One "approximation" I came up with:
$check = array_filter($myArray, function($element) { ... });
if ($check)
//...
The downside of this is that the code's purpose is not immediately clear. Also, it won't stop iterating over the array even if the element was found, although this is more of a nitpick (if the data set is large enough to cause problems, linear search won't be an answer anyway)
To pull the first one from the array, or return false
:
current(array_filter($myArray, function($element) { ... }))
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) { ...
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;
}
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
}
You can write such a function yourself, although it is little more than a loop.
For instance, this function allows you to pass a callback function. The callback can either return 0 or a value. The callback I specify returns the integer if it is > 10. The function stops when the callback returns a non null value.
function check_in_array(array $array, $callback)
{
foreach($array as $item)
{
$value = call_user_func($callback, $item);
if ($value !== null)
return $value;
}
}
$a = array(1, 2, 3, 6, 9, 11, 15);
echo check_in_array($a, function($i){ return ($i > 10?$i:null); });
Pulled from Laravel's Illuminate\Collections\Arr::first
method:
<?php
if (!function_exists('array_first') {
/**
* Return the first element in an array passing a given truth test.
*
* @param iterable $array
* @param callable|null $callback
* @param mixed $default
* @return mixed
*/
function array_first($array, callable $callback = null, $default = null)
{
if (is_null($callback)) {
if (empty($array)) {
return $default;
}
foreach ($array as $item) {
return $item;
}
}
foreach ($array as $key => $value) {
if ($callback($value, $key)) {
return $value;
}
}
return $default;
}
}
I think it's pretty good. There is also the Illuminate\Collections\Arr::last
method, but it's probably not as optimized since it reverses the array and just calls the first
method. It does get the job done though.
<?php
if (!function_exists('array_last')) {
/**
* Return the last element in an array passing a given truth test.
*
* @param array $array
* @param callable|null $callback
* @param mixed $default
* @return mixed
*/
function array_last($array, callable $callback = null, $default = null)
{
if (is_null($callback)) {
return empty($array) ? $default : end($array);
}
return array_first(array_reverse($array, true), $callback, $default);
}
}
Pro tip : If you have an array of objects, then you can specify the type of the callback's argument for that sweet IDE autocompletion.
<?php
$john = array_first($users, function(User $user) {
return $user->name === 'John';
});
// Works with pretty much anything.
$activeUsers = array_filter($users, function(User $user) {
return $user->isActive;
});
// Class example:
class User {
public string $name;
public bool $isActive;
//...
}
You can write your own function ;)
function callback_search ($array, $callback) { // name may vary
return array_filter($array, $callback);
}
This maybe seems useless, but it increases semantics and can increase readability
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.