简体   繁体   English

使用用户定义的 function 搜索 PHP 阵列的优雅方式

[英]Elegant way to search an PHP array using a user-defined function

Basically, I want to be able to get the functionality of C++'s find_if() , Smalltalk's detect: etc.:基本上,我希望能够获得 C++ 的find_if() 、Smalltalk 的detect:等的功能:

// 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.但我不知道有任何 PHP function 可以做到这一点。 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 : 从数组中拉出第一个,或返回false

current(array_filter($myArray, function($element) { ... }))

More info on current() here . 关于current()的更多信息在这里

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. 如果$f($x)返回true ,则会立即返回循环短路和$x 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. array_filter相比,这对我们的用例更好,因为在找到第一个正匹配后, array_find不必继续迭代。

In the event the callback never returns true, a value of null is returned. 如果回调永远不会返回true,则返回null值。


Note, I used call_user_func($f, $x) instead of just calling $f($x) . 注意,我使用call_user_func($f, $x)而不是只调用$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 如果您使用的是PHP 7,请添加一些类型提示

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). 原始array_search返回匹配值的键,而不是值本身(如果您稍后要更改原始数组,这可能很有用)。

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. 使用来自nikic的原始迭代函数\\iter\\search() It has the added benefit that it operates on both arrays and Traversable collections. 它具有额外的好处,它可以在阵列 Traversable集合上运行。

$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. 回调可以返回0或值。 The callback I specify returns the integer if it is > 10. The function stops when the callback returns a non null value. 我指定的回调如果> 10则返回整数。当回调返回非空值时,该函数停止。

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:从 Laravel 的Illuminate\Collections\Arr::first方法中提取:

<?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.还有Illuminate\Collections\Arr::last方法,但它可能没有优化,因为它反转数组并只调用第first方法。 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.专业提示:如果你有一个对象数组,那么你可以为那个甜蜜的 IDE 自动完成指定回调参数的类型。

<?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 这似乎没用,但它增加了语义并且可以提高可读性

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM