Searching for key in multidimensional array and returning path to it

后端 未结 4 916
日久生厌
日久生厌 2021-01-06 12:48

I need to find a specific key in an array, and return both its value and the path to find that key. Example:

$array = array(
  \'fs1\' =>         


        
相关标签:
4条回答
  • 2021-01-06 13:23

    There is already an answer in wich RecursiveIteratorIterator used. My solution might not be always optimal, I have not tested estimation time. It can be optimized by redefining the callHasChildren method of RecursiveIteratorIterator, so there will be no children when key is found. But this is outside the domain.

    Here is the approach where you do not have to use explicit inner loops:

    function findKeyPathAndValue(array $array, $keyToSearch)
    {
        $iterator = new RecursiveIteratorIterator(
            new RecursiveArrayIterator($array),
            RecursiveIteratorIterator::CHILD_FIRST
        );
    
        $path = [];
        $value = null;
        $depthOfTheFoundKey = null;
        foreach ($iterator as $key => $current) {
            if (
                $key === $keyToSearch
                || $iterator->getDepth() < $depthOfTheFoundKey
            ) {
                if (is_null($depthOfTheFoundKey)) {
                    $value = $current;
                }
    
                array_unshift($path, $key);
                $depthOfTheFoundKey = $iterator->getDepth();
            }
        }
    
        if (is_null($depthOfTheFoundKey)) {
            return false;
        }
    
        return [
            'path' => implode('.', $path),
            'value' => $value
        ];
    }
    

    Pay attention to RecursiveIteratorIterator::CHILD_FIRST. This flag reverses the order of iteration. So we can prepare our path using only one loop - this is actually the main purpose of recursive iterators. They hide all the inner loops from you.

    Here is working demo.

    0 讨论(0)
  • 2021-01-06 13:24

    In case you need to return array with all items matching certain key, you can use php generators

    function recursiveFind(array $haystack, string $needle, $glue = '.'): ?\Generator
    {
        $recursive = new \RecursiveIteratorIterator(
            new \RecursiveArrayIterator($haystack),
            \RecursiveIteratorIterator::SELF_FIRST
        );
    
        foreach ($recursive as $key => $value) {
            //if the key matches our search
            if ($key === $needle) {
                //add the current key
                $keys = [$key];
                //loop up the recursive chain
                for ($i = $recursive->getDepth() - 1; $i >= 0; $i--) {
                    array_unshift($keys, $recursive->getSubIterator($i)->key());
                }
    
                yield [
                    'path' => implode($glue, $keys),
                    'value' => $value
                ];
            }
        }
    }
    

    usage:

    foreach (recursiveFind($arrayToSearch, 'keyName') as $result) {
        var_dump($result);
    }
    
    0 讨论(0)
  • 2021-01-06 13:31

    Just for completion sake and future visitors. Combining the example code above and the answer I commented about to get the keys. Here is a working function that will return the requested results with one small change. In my return array I return the keys path and value instead of the requested 0 and $search for the keys. I find this more verbose and easier to handle.

    <?php
    $array = array(
        'fs1' => array(
            'id1' => 0,
            'foo' => 1,
            'fs2' => array(
                'id2' => 1,
                'foo2' => 2,
                'fs3' => array(
                    'id3' => null,
                ),
                'fs4' => array(
                    'id4' => 4,
                    'bar' => 1,
                ),
            ),
        ),
    );
    
    function search($array, $searchKey=''){
        //create a recursive iterator to loop over the array recursively
        $iter = new RecursiveIteratorIterator(
            new RecursiveArrayIterator($array),
            RecursiveIteratorIterator::SELF_FIRST);
    
        //loop over the iterator
        foreach ($iter as $key => $value) {
            //if the key matches our search
            if ($key === $searchKey) {
                //add the current key
                $keys = array($key);
                //loop up the recursive chain
                for($i=$iter->getDepth()-1;$i>=0;$i--){
                    //add each parent key
                    array_unshift($keys, $iter->getSubIterator($i)->key());
                }
                //return our output array
                return array('path'=>implode('.', $keys), 'value'=>$value);
            }
        }
        //return false if not found
        return false;
    }
    
    $searchResult1 = search($array, 'fs2');
    $searchResult2 = search($array, 'fs3');
    echo "<pre>";
    print_r($searchResult1);
    print_r($searchResult2);
    

    outputs:

    Array
    (
        [path] => fs1.fs2
        [value] => Array
            (
                [id2] => 1
                [foo2] => 2
                [fs3] => Array
                    (
                        [id3] => 
                    )
    
                [fs4] => Array
                    (
                        [id4] => 4
                        [bar] => 1
                    )
    
            )
    
    )
    Array
    (
        [path] => fs1.fs2.fs3
        [value] => Array
            (
                [id3] => 
            )
    
    )
    
    0 讨论(0)
  • 2021-01-06 13:37

    It looks like you are assuming the keys will always be unique. I do not assume that. So, your function must return multiple values. What I would do is simply write a recursive function:

    function search($array, $key, $path='')
    {
        foreach($array as $k=>$v)
        {
            if($k == $key) yield array($path==''?$k:$path.'.'.$k, array($k=>$v));
            if(is_array($v))
            { // I don't know a better way to do the following...
                $gen = search($v, $key, $path==''?$k:$path.'.'.$k);
                foreach($gen as $v) yield($v);
            }
        }
    }
    

    This is a recursive generator. It returns a generator, containing all hits. It is used very much like an array:

    $gen = search($array, 'fs3');
    foreach($gen as $ret)
        print_r($ret); // Prints out each answer from the generator
    
    0 讨论(0)
提交回复
热议问题