Элегантный способ поиска массива PHP с использованием пользовательской функции - программирование

Элегантный способ поиска массива PHP с использованием пользовательской функции

В принципе, я хочу иметь возможность получить функциональность С++ find_if(), Smalltalk detect: и т.д.:

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

Но я не знаю никакой функции PHP, которая делает это. Одно "приближение" я придумал:

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

Недостатком этого является то, что цель кода не сразу понятна. Кроме того, он не будет останавливать повторение массива, даже если элемент был найден, хотя это больше похоже на nitpick (если набор данных достаточно велик, чтобы вызвать проблемы, линейный поиск не будет ответом в любом случае)

4b9b3361

Ответ 1

Вы можете написать свою собственную функцию;)

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

Это может показаться бесполезным, но оно увеличивает семантику и может повысить удобочитаемость

Ответ 3

Здесь основное решение

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). Это уместно здесь, потому что оно позволяет использовать любые совместимые 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'

Конечно, он работает и для более сложных структур данных.

$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) { ...

Ответ 4

исходный "array_seach" возвращает ключ значения, а не само значение (это может быть полезно, если вы захотите изменить исходный массив позже).

попробуйте эту функцию (она также будет работать с ассоциативными массивами)

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

    return false;
}

Ответ 5

Вы можете написать такую ​​функцию самостоятельно, хотя это немного больше, чем цикл.

Например, эта функция позволяет вам передать функцию обратного вызова. Обратный вызов может либо возвращать 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); });

Ответ 6

Используйте \iter\search() из nikic iter library из примитивных итерационных функций. Он имеет дополнительное преимущество в том, что он работает как с массивами, так и с коллекциями Traversable.

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

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