Blog

[PHP] Recursive Key Search

Posted on

After unsuccessfully searching for a way to locate a key in a multidimensional array, I decided to write a function that will do this and return the path, element or value associated with the key.

    /**
    * Recursively searches a multidimensional array for a key and optional value and returns the path as a string representation or subset of the array or a value.
    *
    * @author  Akin Williams <aowilliams@arstropica.com>
    *
    * @param   int|string $needle Key
    * @param   array $haystack Array to be searched
    * @param   bool $strict Optional, limit to keys of the same type. Default false.
    * @param   string $output Optional, output key path as a string representation or array subset, ('array'|'string'|'value'). Default array.
    * @param   bool $count Optional, append number of matching elements to result. Default false.
    * @param   int|string $value Optional, limit results to keys matching this value. Default null.
    * @return  array Array containing matching keys and number of matches
    **/
    function multi_array_key_search($needle, $haystack, $strict=false, $output='array', $count=false, $value=null) {
        // Sanity Check
        if(!is_array($haystack))
            return false;

        $resIdx='matchedIdx';
        $prevKey = "";
        $keys = array();
        $num_matches = 0;

        $numargs = func_num_args();
        if ($numargs > 6){
            $arg_list = func_get_args();
            $keys = $arg_list[6];
            $prevKey = $arg_list[7];
        }

        $keys[$resIdx] = isset($keys[$resIdx]) ? $keys[$resIdx] : 0;

        foreach($haystack as $key => $val) {
            if(is_array($val)) {
                if ((($key === $needle) && is_null($value)) || (($key === $needle) && ($val[$key] == $value) && $strict === false) || (($key === $needle) && ($val[$key] === $value) && $strict === true)){
                    if ($output == 'value'){
                        $keys[$keys[$resIdx]] = $val;
                    } else {
                        $keys[$keys[$resIdx]] = $prevKey . (isset($keys[$keys[$resIdx]]) ? $keys[$keys[$resIdx]] : "") . "["$key"]";
                    }
                    $keys[$resIdx] ++;
                }
                $passedKey = $prevKey . "["$key"]";;
                $keys = multi_array_key_search($needle, $val, $strict, $output, true, $value, $keys, $passedKey);
            } else {
                if ((($key === $needle) && is_null($value)) || (($key === $needle) && ($val == $value) && $strict === false) || (($key === $needle) && ($val === $value) && $strict === true)){
                    if ($output == 'value'){
                        $keys[$keys[$resIdx]] = $val;
                    } else {
                        $keys[$keys[$resIdx]] = $prevKey . (isset($keys[$keys[$resIdx]]) ? $keys[$keys[$resIdx]] : "") . "["$key"]";
                    }
                    $keys[$resIdx] ++;
                }
            }
        }
        if ($numargs < 7){
            $num_matches = (count($keys) == 1) ? 0 : $keys[$resIdx];
            if ($count) $keys['num_matches'] = $num_matches;
            unset($keys[$resIdx]);
            if (($output == 'array') && $num_matches > 0){
                if (is_null($value)) {
                    $replacements = multi_array_key_search($needle, $haystack, $strict, 'value', false);
                }
                $arrKeys = ($count) ? array('num_matches' => $num_matches) : array();
                for ($i=0; $i < $num_matches; $i ++){
                    $keysArr = explode(',', str_replace(array('][', '[', ']'), array(',', '', ''), $keys[$i]));
                    $json = "";
                    foreach($keysArr as $nestedkey){
                        $json .= "{" . $nestedkey . ":";
                    }
                    if (is_null($value)){
                        $placeholder = time();
                        $json .= ""$placeholder"";
                    } else {
                        $json .= ""$value"";
                    }
                    foreach($keysArr as $nestedkey){
                        $json .= "}";
                    }
                    $arrKeys[$i] = json_decode($json, true);
                    if (is_null($value)) {
                        array_walk_recursive($arrKeys[$i], function (&$item, $key, &$userdata) {
                            if($item == $userdata['placeholder'])
                                $item = $userdata['replacement'];
                        }, array('placeholder' => $placeholder, 'replacement' => $replacements[$i]));
                    }
                }
                $keys = $arrKeys;
            }
        }
        return $keys;
    }