let*_*tar 28 php collections functional-programming
基本上,我希望能够获得C++ find_if(),Smalltalk detect:等的功能:
// would return the element or null
check_in_array($myArray, function($element) { return $elemnt->foo() > 10; });
Run Code Online (Sandbox Code Playgroud)
但我不知道有任何PHP函数可以做到这一点.我提出了一个"近似":
$check = array_filter($myArray, function($element) { ... });
if ($check)
//...
Run Code Online (Sandbox Code Playgroud)
这样做的缺点是代码的目的不是立即清楚的.此外,即使找到该元素,它也不会停止迭代数组,尽管这更像是一个挑剔(如果数据集足够大而导致问题,线性搜索无论如何都不会是答案)
Izk*_*ata 45
从数组中拉出第一个,或返回false:
current(array_filter($myArray, function($element) { ... }))
Run Code Online (Sandbox Code Playgroud)
Tha*_*you 29
这是一个基本的解决方案
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
Run Code Online (Sandbox Code Playgroud)
在事件$f($x)返回时true,循环短路并$x立即返回.相比之下array_filter,这对我们的用例更好,因为array_find在找到第一个正匹配后不必继续迭代.
如果回调永远不会返回true,null则返回值.
注意,我使用call_user_func($f, $x)而不是只是打电话$f($x).这在这里是合适的,因为它允许您使用任何兼容的可调用对象
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'
Run Code Online (Sandbox Code Playgroud)
当然它也适用于更复杂的数据结构
$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
// )
Run Code Online (Sandbox Code Playgroud)
如果您使用的是PHP 7,请添加一些类型提示
function array_find(array $xs, callable $f) { ...
Run Code Online (Sandbox Code Playgroud)
原始array_search返回匹配值的键,而不是值本身(如果您以后要更改原始数组,这可能很有用)。
试试这个函数(它也适用于关联数组)
function array_search_func(array $arr, $func)
{
foreach ($arr as $key => $v)
if ($func($v))
return $key;
return false;
}
Run Code Online (Sandbox Code Playgroud)
从 Laravel 的方法中提取Illuminate\Collections\Arr::first:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
我觉得还不错。还有该Illuminate\Collections\Arr::last方法,但它可能没有那么优化,因为它反转数组并只调用该first方法。不过,它确实完成了工作。
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);
}
}
Run Code Online (Sandbox Code Playgroud)
专业提示:如果您有一个对象数组,那么您可以为该可爱的 IDE 自动完成指定回调参数的类型。
$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;
//...
}
Run Code Online (Sandbox Code Playgroud)
如果你想使用外部作用域中的某个变量,你可以使用use(&$var)这样的语法
foreach($values as $key => $value) {
array_find($conditionsRecords, function($row) use(&$key) {
$keyToFind = $key;
return $keyToFind;
})
}
Run Code Online (Sandbox Code Playgroud)
Kin*_*nch -91
您可以编写自己的函数;)
function callback_search ($array, $callback) { // name may vary
return array_filter($array, $callback);
}
Run Code Online (Sandbox Code Playgroud)
这可能看起来没什么用,但它增加了语义并可以提高可读性