簡體   English   中英

使用用戶定義的 function 搜索 PHP 陣列的優雅方式

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

基本上,我希望能夠獲得 C++ 的find_if() 、Smalltalk 的detect:等的功能:

// would return the element or null
check_in_array($myArray, function($element) { return $elemnt->foo() > 10; });

但我不知道有任何 PHP function 可以做到這一點。 我想出了一個“近似值”:

$check = array_filter($myArray, function($element) { ... });
if ($check) 
    //...

這樣做的缺點是代碼的目的不是很清楚。 此外,即使找到了元素,它也不會停止對數組的迭代,盡管這更像是一個挑剔(如果數據集大到足以引起問題,線性搜索無論如何都不會成為答案)

從數組中拉出第一個,或返回false

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

關於current()的更多信息在這里

這是一個基本的解決方案

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

如果$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'

當然它也適用於更復雜的數據結構

$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
// )

如果您使用的是PHP 7,請添加一些類型提示

function array_find(array $xs, callable $f) { ...

原始array_search返回匹配值的鍵,而不是值本身(如果您稍后要更改原始數組,這可能很有用)。

試試這個功能(它也可以用於關聯數組)

function array_search_func(array $arr, $func)
{
    foreach ($arr as $key => $v)
        if ($func($v))
            return $key;

    return false;
}

使用來自nikic的原始迭代函數\\iter\\search() 它具有額外的好處,它可以在陣列 Traversable集合上運行。

$foundItem = \iter\search(function ($item) {
    return $item > 10;
}, range(1, 20));

if ($foundItem !== null) {
    echo $foundItem; // 11
}

你可以自己編寫這樣一個函數,雖然它只不過是一個循環。

例如,此函數允許您傳遞回調函數。 回調可以返回0或值。 我指定的回調如果> 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); });

從 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;
    }
}

我覺得還不錯。 還有Illuminate\Collections\Arr::last方法,但它可能沒有優化,因為它反轉數組並只調用第first方法。 它確實完成了工作。

<?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);
    }
}

專業提示:如果你有一個對象數組,那么你可以為那個甜蜜的 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;
    //...
}

你可以編寫自己的函數;)

function callback_search ($array, $callback) { // name may vary
    return array_filter($array, $callback);
}

這似乎沒用,但它增加了語義並且可以提高可讀性

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM