views:

8243

answers:

5

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.

+6  A: 

Code:

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

    if (is_array($array))
    {
        if ($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 ($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).

John Kugelman
+2  A: 

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.

jared
A: 

I needed something similar, but to search for multidimensional array by value... I took John example and wrote

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

I hope it helps somebody :)

confiq
A: 

How can John's answer be rewritten so that the original keys are preserved and new ones are not created?

Chris Carvache
A: 

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

A minor imporvement to the fast version.

blackmogu