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

Viewed 353005

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.

17 Answers

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

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.

I think the easiest way is using php array functions if you know your key.

function search_array ( $array, $key, $value )
{
   return array_search($value,array_column($array,$key));
}

this return an index that you could find your desired data by this like below:

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

echo json_encode($arr[search_array($arr,'name','cat 2')]);

this output will:

{"id":2,"name":"cat 2"}

If you want to search for array of keys this is good

function searchKeysInMultiDimensionalArray($array, $keys)
{
    $results = array();

    if (is_array($array)) {
        $resultArray = array_intersect_key($array, array_flip($keys));
        if (!empty($resultArray)) {
            $results[] = $resultArray;
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
        }
    }

    return $results;
}

Keys will not overwrite because each set of key => values will be in separate array in resulting array.
If you don't want duplicate keys then use this one

function searchKeysInMultiDimensionalArray($array, $keys)
{
    $results = array();

    if (is_array($array)) {
        $resultArray = array_intersect_key($array, array_flip($keys));
        if (!empty($resultArray)) {
            foreach($resultArray as $key => $single) {

                $results[$key] = $single;
            }
        }

        foreach ($array as $subarray) {
            $results = array_merge($results, searchKeysInMultiDimensionalArray($subarray, $keys));
        }
    }

    return $results;
}

2 functions: array_search_key_value which returns the array of keys to reach a key with a value in a multidimensional array, array_extract_keys which returns the value in a multidimensional array pointed to by an array of keys.

function array_search_key_value($array, $key, $value) {
    if (!is_array($array)) {
        return false;
    }

    return array_search_key_value_aux($array, $key, $value);
}

function array_search_key_value_aux($array, $key, $value, $path=null) {
    if (array_key_exists($key, $array) && $array[$key] === $value) {
        $path[]=$key;

        return $path;
    }

    foreach ($array as $k => $v ) {
        if (is_array($v)) {
            $path[]=$k;

            $p = array_search_key_value_aux($v, $key, $value, $path);

            if ($p !== false) {
                return $p;
            }
        }
    }

    return false;
}

function array_extract_keys($array, $key_list) {
    $v = $array;

    foreach ($key_list as $key) {
        if (!is_array($v) || !array_key_exists($key, $v))
            return false;

        $v = &$v[$key];
    } 
       
    return $v;
}

Here is a unitary test:

$test_array = array(
    'a' => array(
        'aa' => true,
        'ab' => array(
            'aaa' => array(
                'one' => 1,
                'two' => 2,
                'three' => 3,
                'four' => 4
            ),
            'four' => 4,
            'five' => 5,
        ),
        'six' => 6,
    ),
    'seven' => 7
);

$test_data = array(
    array('one', 1),
    array('two', 2),
    array('three', 3),
    array('four', 4),
    array('five', 5),
    array('six', 6),
    array('seven', 7),
    array('zero', 0),
    array('one', 0),
);

foreach ($test_data as $d) {
    $r = array_search_key_value($test_array, $d[0], $d[1]);

    echo $d[0] . ' => ' . $d[1] . ' ? ', $r ? implode('/', $r) . ' => ' . array_extract_keys($test_array, $r) : 'null', PHP_EOL;
}
Related