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

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

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

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

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

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

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

Ответ 1

код:

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 берет свой четвертый параметр по ссылке, а не по значению; амперсанд & имеет решающее значение.

FYI: Если у вас установлена ​​более старая версия PHP, вам нужно указать часть "pass-by-reference" в вызове search_r, а не в ее объявлении. То есть последняя строка становится search_r($subarray, $key, $value, &$results).

Ответ 2

Как насчет 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 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 посвящены некоторым полезным деталям, особенно Pro PHP; и вы можете, вероятно, google и получить дополнительную информацию.

Ответ 3

<?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

Ответ 4

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

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

Мне просто понадобилась функция STOP для поиска и возврата, когда совпадение найдено. То есть, если вы ищете для customer_id, мы знаем, что у нас есть только один в наборе результатов, и как только мы найдем client_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 секунд. Все еще очень дорого, но гораздо разумнее.

Ответ 5

if (isset($array[$key]) && $array[$key] == $value)

Небольшое преимущество быстрой версии.

Ответ 6

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

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

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

Ответ 8

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

Ответ 9

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

Ответ 10

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

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

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

Ответ 11

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

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'));

Ответ 12

Вот решение:

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

?>

Ответ 13

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

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

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

Ответ 14

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