How to search by key=>value in a multidimensional array in PHP

344,915

Solution 1

Code:

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

Output:

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

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

)

If efficiency is important you could write it so all the recursive calls store their results in the same temporary $results array rather than merging arrays together, like so:

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

The key there is that search_r takes its fourth parameter by reference rather than by value; the ampersand & is crucial.

FYI: If you have an older version of PHP then you have to specify the pass-by-reference part in the call to search_r rather than in its declaration. That is, the last line becomes search_r($subarray, $key, $value, &$results).

Solution 2

How about the SPL version instead? It'll save you some typing:

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

What's great is that basically the same code will iterate through a directory for you, by using a RecursiveDirectoryIterator instead of a RecursiveArrayIterator. SPL is the roxor.

The only bummer about SPL is that it's badly documented on the web. But several PHP books go into some useful detail, particularly Pro PHP; and you can probably google for more info, too.

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

?>

Ref: http://php.net/manual/en/function.array-filter.php

Solution 4

Came back to post this update for anyone needing an optimisation tip on these answers, particulary John Kugelman's great answer up above.

His posted function work fine but I had to optimize this scenario for handling a 12 000 row resultset. The function was taking an eternal 8 secs to go through all records, waaaaaay too long.

I simply needed the function to STOP searching and return when match was found. Ie, if searching for a customer_id, we know we only have one in the resultset and once we find the customer_id in the multidimensional array, we want to return.

Here is the speed-optimised ( and much simplified ) version of this function, for anyone in need. Unlike other version, it can only handle only one depth of array, does not recurse and does away with merging multiple results.

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

This brought down the the task to match the 12 000 records to a 1.5 secs. Still very costly but much more reasonable.

Solution 5

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

A minor imporvement to the fast version.

Share:
344,915

Related videos on Youtube

Admin
Author by

Admin

Updated on January 28, 2022

Comments

  • Admin
    Admin over 2 years

    Is there any fast way to get all subarrays where a key value pair was found in a multidimensional array? I can't say how deep the array will be.

    Simple example array:

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

    When I search for key=name and value="cat 1" the function should return:

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

    I guess the function has to be recursive to get down to the deepest level.

  • kronwied
    kronwied over 13 years
    This works like a charm and I plan use it again for similar problems :D The only weird part is in the foreach and using the getSubIterator function on the RecursiveIteratorIterator instead of the $sub variable. I thought it was a typo at first but it's the right way! thanks Jared.
  • stefgosselin
    stefgosselin almost 13 years
    Actually this prevents it from throwing warnings when the key is not set. Not so minor! -> +1'ed.
  • codercake
    codercake over 12 years
    agreed, being able to actually glance through the php error log for major errors and not have it polluted with warnings is the way to go in my opinion.
  • trante
    trante about 12 years
    Thank you for solution. Where do we get the "id"? From $outputArray?
  • Drew
    Drew over 11 years
    This answer should be correct. Although the brute force search method will do it, this is much less resource intensive.
  • Mahesh.D
    Mahesh.D almost 11 years
    Thanks,very straight forward solution,but don't know about performance ??.
  • Awena
    Awena almost 9 years
    this one is faster than Jhon/Jared's answer (0.0009999275207519) vs (0.0020008087158203).. Well this test is specific to my case and environment.. Im sticking with this, thanks stefgosselin
  • orrd
    orrd almost 9 years
    This is a good solution if you want to search an array that is only one level deep, but this particular question was about searching recursively into a deep array ("the function has to be recursive to get down to the deepest level").
  • orrd
    orrd almost 9 years
    It should be noted that what you're suggesting only makes sense if you're searching the same array many times. It takes far longer to go through the trouble of sorting it (O(n log n)) than it does to simply do a linear search for the value (O(n)). But once it's sorted, sure, then a binary search would be faster.
  • orrd
    orrd almost 9 years
    I should also add that using objects instead of arrays may be a useful abstraction, but you could also do a binary search on an array if the array is sorted. You don't need to use objects to sort an array or to do a binary search on it.
  • shyammakwana.me
    shyammakwana.me over 8 years
    my case is different but got hint from your answer.
  • Fr0zenFyr
    Fr0zenFyr over 8 years
    how to unset the found element(could be a sub-array) from original array?
  • Rich Benner
    Rich Benner about 7 years
    Could you expand on this answer? Code only answers don't explain what you're actually doing.
  • mickmackusa
    mickmackusa over 6 years
    Please update your question with the intent to educate.
  • Giovanny Gonzalez
    Giovanny Gonzalez over 5 years
    This is functional for only find Key, This works for me.
  • mickmackusa
    mickmackusa over 4 years
    This is not a complete solution and so is more of an "Attempt To Respond To Another Post" and "Not An Answer".
  • clockw0rk
    clockw0rk almost 3 years
    it would only take a single operation for needle @ [100][1] if anybody used correct implemented multithreading. start a thread from [0][0] forward, from [100][1] backward, from [50][1] backward and from [50][0] forward. you can basically reduce search time by adding more threads as long as your processor does not meltdown ^^