Как выполнить поиск по ключу=>значение в многомерном массиве в PHP


Есть ли какой-нибудь быстрый способ получить все подмассивы, в которых была найдена пара значений ключа в многомерном массиве? Я не могу сказать, насколько глубоким будет массив.

Простой пример массива:

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1")
);

Когда я ищу ключ=имя и значение="cat 1", функция должна возвращать:

array(0 => array(id=>1,name=>"cat 1"),
      1 => array(id=>3,name=>"cat 1")
);

Я предполагаю, что функция должна быть рекурсивной, чтобы спуститься на самый глубокий уровень.

Author: Aaron Harun, 2009-06-19

14 answers

Код:

function search($array, $key, $value)
{
    $results = array();

    if (is_array($array)) {
        if (isset($array[$key]) && $array[$key] == $value) {
            $results[] = $array;
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, search($subarray, $key, $value));
        }
    }

    return $results;
}

$arr = array(0 => array(id=>1,name=>"cat 1"),
             1 => array(id=>2,name=>"cat 2"),
             2 => array(id=>3,name=>"cat 1"));

print_r(search($arr, 'name', 'cat 1'));

Вывод:

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => cat 1
        )

    [1] => Array
        (
            [id] => 3
            [name] => cat 1
        )

)

Если важна эффективность, вы могли бы написать ее так, чтобы все рекурсивные вызовы сохраняли свои результаты в одном и том же временном массиве $results, а не объединяли массивы вместе, например:

function search($array, $key, $value)
{
    $results = array();
    search_r($array, $key, $value, $results);
    return $results;
}

function search_r($array, $key, $value, &$results)
{
    if (!is_array($array)) {
        return;
    }

    if (isset($array[$key]) && $array[$key] == $value) {
        $results[] = $array;
    }

    foreach ($array as $subarray) {
        search_r($subarray, $key, $value, $results);
    }
}

Ключ в том, что search_r принимает свой четвертый параметр по ссылке, а не по значению; амперсанд & имеет решающее значение.

К вашему сведению: Если у вас более старая версия PHP, то вам необходимо указать ссылку на ссылку в вызовите в search_r, а не в его объявлении. То есть последняя строка становится search_r($subarray, $key, $value, &$results).

 192
Author: John Kugelman, 2014-03-05 15:39:13

Как насчет версии SPL вместо этого? Это сэкономит вам время на наборе текста:

// I changed your input example to make it harder and
// to show it works at lower depths:

$arr = array(0 => array('id'=>1,'name'=>"cat 1"),
             1 => array(array('id'=>3,'name'=>"cat 1")),
             2 => array('id'=>2,'name'=>"cat 2")
);

//here's the code:

    $arrIt = new RecursiveIteratorIterator(new RecursiveArrayIterator($arr));

 foreach ($arrIt as $sub) {
    $subArray = $arrIt->getSubIterator();
    if ($subArray['name'] === 'cat 1') {
        $outputArray[] = iterator_to_array($subArray);
    }
}

Что замечательно, так это то, что в основном один и тот же код будет проходить итерацию по каталогу для вас, используя RecursiveDirectoryIterator вместо RecursiveArrayIterator. SPL - это роксор.

Единственный облом в SPL заключается в том, что он плохо документирован в Интернете. Но несколько книг по PHP посвящены некоторым полезным деталям, особенно про PHP; и вы, вероятно, тоже можете поискать в Google дополнительную информацию.

 68
Author: jared, 2009-06-19 19:26:52
<?php
$arr = array(0 => array("id"=>1,"name"=>"cat 1"),
             1 => array("id"=>2,"name"=>"cat 2"),
             2 => array("id"=>3,"name"=>"cat 1")
);
$arr = array_filter($arr, function($ar) {
   return ($ar['name'] == 'cat 1');
   //return ($ar['name'] == 'cat 1' AND $ar['id'] == '3');// you can add multiple conditions
});

echo "<pre>";
print_r($arr);

?>

Ссылка: http://php.net/manual/en/function .array-filter.php

 37
Author: Prasanth Bendra, 2017-02-17 10:25:28

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

Его опубликованная функция работает нормально, но мне пришлось оптимизировать этот сценарий для обработки набора результатов из 12 000 строк. Функция занимала вечные 8 секунд, чтобы просмотреть все записи, ваааааай слишком долго.

Мне просто нужна была функция, чтобы ОСТАНОВИТЬ поиск и вернуться, когда совпадение будет найдено. Т. Е., если вы ищете идентификатор customer_id, мы знаем, что мы только есть один в наборе результатов, и как только мы найдем идентификатор customer_id в многомерном массиве, мы хотим вернуться.

Вот оптимизированная по скорости (и значительно упрощенная) версия этой функции для всех, кто в ней нуждается. В отличие от другой версии, он может обрабатывать только одну глубину массива, не рекурсирует и устраняет слияние нескольких результатов.

// search array for specific key = value
public function searchSubArray(Array $array, $key, $value) {   
    foreach ($array as $subarray){  
        if (isset($subarray[$key]) && $subarray[$key] == $value)
          return $subarray;       
    } 
}

Это снизило задачу сопоставления 12 000 записей до 1,5 секунд. Все еще очень дорого но гораздо больше разумно.

 16
Author: stefgosselin, 2011-06-10 07:02:53
if (isset($array[$key]) && $array[$key] == $value)

Незначительное усовершенствование быстрой версии.

 14
Author: blackmogu, 2013-09-30 16:37:53

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

array(
    [0] => array ([0] => something, [1] => something_else))
    ...
    [100] => array ([0] => something100, [1] => something_else100))
)

Потребуется не более 200 итераций, чтобы найти то, что вы ищете (если бы стрелка была на [100][1]), с подходящим алгоритмом.

Линейные алгоритмы в этом случае выполняются при O(n) (общее количество элементов порядка во всем массиве), это бедный, миллион записей (например, массив 1000x100x10) потребовал бы в среднем 500 000 итераций, чтобы найти иглу. Кроме того, что произойдет, если вы решите изменить структуру своего многомерного массива? И PHP запустил бы рекурсивный алгоритм, если бы ваша глубина была больше 100. Информатика может сделать лучше:

По возможности всегда используйте объекты вместо многомерных массивов:

ArrayObject(
   MyObject(something, something_else))
   ...
   MyObject(something100, something_else100))
)

И примените пользовательский интерфейс и функцию компаратора для сортировки и поиска они:

interface Comparable {
   public function compareTo(Comparable $o);
}

class MyObject implements Comparable {
   public function compareTo(Comparable $o){
      ...
   }
}

function myComp(Comparable $a, Comparable $b){
    return $a->compareTo($b);
}

Вы можете использовать uasort() для использования пользовательского компаратора, если вы чувствуете себя предприимчивым, вам следует реализовать свои собственные коллекции для своих объектов, которые могут сортировать их и управлять ими (я всегда расширяю ArrayObject, чтобы включить функцию поиска по крайней мере).

$arrayObj->uasort("myComp");

Как только они отсортированы (uasort - это O(n log n), что так же хорошо, как и для произвольных данных), двоичный поиск может выполнить операцию за O(log n) времени, т. Е. для поиска миллиона записей требуется всего ~20 итераций. Как насколько мне известно, пользовательский двоичный поиск компаратора не реализован в PHP (array_search() использует естественный порядок, который работает со ссылками на объекты, а не с их свойствами), вам придется реализовать это самостоятельно, как и я.

Этот подход более эффективен (больше нет глубины) и, что более важно, универсален (при условии, что вы обеспечиваете сопоставимость с помощью интерфейсов), поскольку объекты определяют, как они сортируются, поэтому вы можете бесконечно перерабатывать код. Намного лучше =)

 7
Author: mbdxgdb2, 2012-06-15 04:16:06
$result = array_filter($arr, function ($var) {   
  $found = false;
  array_walk_recursive($var, function ($item, $key) use (&$found) {  
    $found = $found || $key == "name" && $item == "cat 1";
  });
  return $found;
});
 5
Author: Vitalii Fedorenko, 2012-01-25 15:23:11

Http://snipplr.com/view/51108/nested-array-search-by-value-or-key/

<?php

//PHP 5.3

function searchNestedArray(array $array, $search, $mode = 'value') {

    foreach (new RecursiveIteratorIterator(new RecursiveArrayIterator($array)) as $key => $value) {
        if ($search === ${${"mode"}})
            return true;
    }
    return false;
}

$data = array(
    array('abc', 'ddd'),
    'ccc',
    'bbb',
    array('aaa', array('yyy', 'mp' => 555))
);

var_dump(searchNestedArray($data, 555));
 3
Author: Pramendra Gupta, 2011-03-25 01:42:43
function in_multi_array($needle, $key, $haystack) 
{
    $in_multi_array = false;
    if (in_array($needle, $haystack))
    {
        $in_multi_array = true; 
    }else 
    {
       foreach( $haystack as $key1 => $val )
       {
           if(is_array($val)) 
           {
               if($this->in_multi_array($needle, $key, $val)) 
               {
                   $in_multi_array = true;
                   break;
               }
           }
        }
    }

    return $in_multi_array;
} 
 3
Author: radhe, 2011-05-23 18:09:08

Мне нужно было что-то подобное, но для поиска многомерного массива по значению... Я взял пример с Джона и написал

function _search_array_by_value($array, $value) {
        $results = array();
        if (is_array($array)) {
            $found = array_search($value,$array);
            if ($found) {
                $results[] = $found;
            }
            foreach ($array as $subarray)
                $results = array_merge($results, $this->_search_array_by_value($subarray, $value));
        }
        return $results;
    }

Надеюсь, это кому-нибудь поможет:)

 2
Author: confiq, 2010-04-09 11:05:28

Это пересмотренная функция по сравнению с той, которую опубликовал Джон К... Мне нужно захватить только определенный ключ в массиве и ничего выше него.

function search_array ( $array, $key, $value )
{
    $results = array();

    if ( is_array($array) )
    {
        if ( $array[$key] == $value )
        {
            $results[] = $array;
        } else {
            foreach ($array as $subarray) 
                $results = array_merge( $results, $this->search_array($subarray, $key, $value) );
        }
    }

    return $results;
}

$arr = array(0 => array(id=>1,name=>"cat 1"),
       1 => array(id=>2,name=>"cat 2"),
       2 => array(id=>3,name=>"cat 1"));

print_r(search_array($arr, 'name', 'cat 1'));
 2
Author: Trevor Lettman, 2012-02-09 17:14:46

Вот решение:

<?php
$students['e1003']['birthplace'] = ("Mandaluyong <br>");
$students['ter1003']['birthplace'] = ("San Juan <br>");
$students['fgg1003']['birthplace'] = ("Quezon City <br>");
$students['bdf1003']['birthplace'] = ("Manila <br>");

$key = array_search('Delata Jona', array_column($students, 'name'));
echo $key;  

?>
 2
Author: Tristan, 2017-01-16 07:16:04

И другая версия, которая возвращает значение ключа из элемента массива, в котором найдено значение (без рекурсии, оптимизировано для скорости):

// if the array is 
$arr['apples'] = array('id' => 1);
$arr['oranges'] = array('id' => 2);

//then 
print_r(search_array($arr, 'id', 2);
// returns Array ( [oranges] => Array ( [id] => 2 ) ) 
// instead of Array ( [0] => Array ( [id] => 2 ) )

// search array for specific key = value
function search_array($array, $key, $value) {
  $return = array();   
  foreach ($array as $k=>$subarray){  
    if (isset($subarray[$key]) && $subarray[$key] == $value) {
      $return[$k] = $subarray;
      return $return;
    } 
  }
}

Спасибо всем, кто разместил здесь.

 1
Author: Darko Hrgovic, 2011-09-22 00:31:58
function findKey($tab, $key){
    foreach($tab as $k => $value){ 
        if($k==$key) return $value; 
        if(is_array($value)){ 
            $find = findKey($value, $key);
            if($find) return $find;
        }
    }
    return null;
}
 1
Author: Monaem AMINA, 2017-05-08 09:30:17