Get all parent nodes with RecursiveArrayIterator

一笑奈何 提交于 2020-01-06 07:31:05

问题


Essentially, I want to use the

$foo = new RecursiveIteratorIterator(new RecursiveArrayIterator($haystack));

Methodology, but instead of returning a flat array for foreach()ing through, keep the structure but only return a single great-grand-child node and it's parent nodes. Is this possible in PHP?


I've been tasked with optimising some of my company's (horrific) codebase. I found a function that recurses through an array, searching for a key. I can't replace this with a simple array_search() or array_key_exists() because the custom function also returns the parent nodes of the matched (found) key, instead of just a true or false.

How can I use RecursiveArrayIterator, RecursiveIteratorIterator or failing that, other built-in functions (i.e. as little custom code as possible) to return a matching node with it's parent tree from a search array? I want to get the fastest function possible, as currently this function spends 8 seconds executing 14 thousand times, hence my requirement to use built-in functions.

My existing attempt (below) is incredibly slow.

function search_in_array($needle, $haystack) {
    $path = array();

    $it = new RecursiveArrayIterator($haystack);
    iterator_apply($it, 'traverse', array($it, $needle, &$path));

    return $path;
}

function traverse($it, $needle, &$path) {
    while($it->valid()) {
        $key = $it->key();
        $value = $it->current();

        if(strcasecmp($value['id'], $needle) === 0) {
            $path[] = $key;
            return;
        } else if($it->hasChildren()) {
            $sub = null;
            traverse($it->getChildren(), $needle, &$sub);
            if($sub) {
                $path[$key] = $sub;
            }
        }

        $it->next();
    }
}

Example output for $needle = TVALL would look like this:

Array (
    [HOMECINEMA] => Array (
        [children] => Array (
            [HC-VISION] => Array (
                [children] => Array (
                    [0] => TVALL
                )
            )
        )
    )
)

The search array looks something like this (sorry for the vast-ness). There are more than two top-level nodes, but I've truncated it for brevity:

Array(2) (
    [HOMECINEMA] => Array (
        [id] => HOMECINEMA
        [position] => 2
        [title] => TV & Home Cinema
        [children] => Array (
            [HC-VISION] => Array (
                [id] => HC-VISION
                [title] => LCD & Plasma
                [children] => Array (
                    [TVALL] => Array (
                        [id] => TVALL
                        [title] => All TVs
                    )
                    [LCD2] => Array (
                        [id] => LCD2
                        [title] => LCD/LED TVs
                    )
                    [PLASMA] => Array (
                        [id] => PLASMA
                        [title] => Plasma TVs
                    )
                    [3DTV] => Array (
                        [id] => 3DTV
                        [title] => 3D TV
                    )
                    [LED] => Array (
                        [id] => LED
                        [title] => SMART TVs
                    )
                    [PROJECTORS] => Array (
                        [id] => PROJECTORS
                        [title] => Projectors
                    )
                    [SYS-HOMECINEMATV] => Array (
                        [id] => SYS-HOMECINEMATV
                        [title] => TV Home Cinema Systems
                    )
                )
            )
            [HC-SEPARATES] => Array (
                [id] => HC-SEPARATES
                [title] => Home Cinema Separates
                [children] => Array (
                    [DVDRECORDERS] => Array (
                        [id] => DVDRECORDERS
                        [title] => DVD Recorders
                    )
                    [HDDVD] => Array (
                        [id] => HDDVD
                        [title] => Blu-ray
                    )
                    [AVRECEIVERS] => Array (
                        [id] => AVRECEIVERS
                        [title] => AV Receivers
                    )
                    [DVDPLAYERS] => Array (
                        [id] => DVDPLAYERS
                        [title] => DVD Players
                    )
                    [FREEVIEW] => Array (
                        [id] => FREEVIEW
                        [title] => Digital Set Top Boxes
                    )
                    [DVDPACKAGESYSTEMS-3] => Array (
                        [id] => DVDPACKAGESYSTEMS-3
                        [title] => 1 Box Home Cinema Systems
                    )
                    [HOMECINEMADEALS] => Array (
                        [id] => HOMECINEMADEALS
                        [title] => Home Cinema System Deals
                    )
                )
            )
            [SPEAKER2] => Array (
                [id] => SPEAKER2
                [title] => Speakers
                [children] => Array (
                    [SPEAKERPACKAGES] => Array (
                        [id] => SPEAKERPACKAGES
                        [title] => Speaker packages
                    )
                    [SOUNDBARS] => Array (
                        [id] => SOUNDBARS
                        [title] => Soundbars
                    )
                    [CENTRES] => Array (
                        [id] => CENTRES
                        [title] => Centres
                    )
                    [SUBWOOFERS] => Array (
                        [id] => SUBWOOFERS
                        [title] => Subwoofers
                    )
                    [FLOORSTANDING] => Array (
                        [id] => FLOORSTANDING
                        [title] => Floorstanders
                    )
                    [INSTALLATIONSPEAKERS] => Array (
                        [id] => INSTALLATIONSPEAKERS
                        [title] => Installation Speakers
                    )
                    [STAND-MOUNT] => Array (
                        [id] => STAND-MOUNT
                        [title] => Bookshelf Speakers
                    )
                )
            )
            [HC-ACCYS] => Array (
                [id] => HC-ACCYS
                [title] => Accessories
                [children] => Array (
                    [AVESSENTIALS] => Array (
                        [id] => AVESSENTIALS
                        [title] => AV Interconnects
                    )
                    [PLASMALCDSTANDSBRACKETS1] => Array (
                        [id] => PLASMALCDSTANDSBRACKETS1
                        [title] => TV Accessories
                    )
                    [RACKS] => Array (
                        [id] => RACKS
                        [title] => TV Racks
                    )
                    [HIFIRACKS] => Array (
                        [id] => HIFIRACKS
                        [title] => HiFi Racks
                    )
                    [PROJECTORACCYS] => Array (
                        [id] => PROJECTORACCYS
                        [title] => Projector Screens/Accessories
                    )
                )
            )
        )
    )
    [SPEAKERS] => Array (
        [id] => SPEAKERS
        [position] => 3
        [title] => Speakers
        [children] => Array (
            [SPK-HIFI] => Array (
                [id] => SPK-HIFI
                [title] => Hi-Fi
                [children] => Array (
                    [STAND-MOUNT] => Array (
                        [id] => STAND-MOUNT
                        [title] => Bookshelf Speakers
                    )
                    [FLOORSTANDING] => Array (
                        [id] => FLOORSTANDING
                        [title] => Floorstanders
                    )
                    [INSTALLATIONSPEAKERS] => Array (
                        [id] => INSTALLATIONSPEAKERS
                        [title] => Installation Speakers
                    )
                )
            )
            [SPK-HOMECINEMA] => Array (
                [id] => SPK-HOMECINEMA
                [title] => Home Cinema
                [children] => Array (
                    [SPEAKERPACKAGES] => Array (
                        [id] => SPEAKERPACKAGES
                        [title] => Speaker Packages
                    )
                    [SOUNDBARS] => Array (
                        [id] => SOUNDBARS
                        [title] => Soundbars
                    )
                    [CENTRES] => Array (
                        [id] => CENTRES
                        [title] => Centres
                    )
                    [SUBWOOFERS] => Array (
                        [id] => SUBWOOFERS
                        [title] => Subwoofers
                    )
                )
            )
            [SPK-ACCYS] => Array (
                [id] => SPK-ACCYS
                [title] => Accessories
                [children] => Array (
                    [SPEAKERESSENTIALS1] => Array (
                        [id] => SPEAKERESSENTIALS
                        [title] => Speaker Cables
                    )
                    [SPEAKERSTANDS] => Array (
                        [id] => SPEAKERSTANDS
                        [title] => Speaker Stands
                    )
                    [SPEAKERBRACKETS] => Array (
                        [id] => SPEAKERBRACKETS
                        [title] => Speaker Wall Brackets
                    )
                )
            )
        )
    )
)

回答1:


The example below will not necessarily be more performant (in time or memory requirements) but avoids manually recursing through the structure and shows an easier (IMHO) way to build your desired output array.

function search_in_array($needle, $haystack) {
    $path = array();

    $it = new RecursiveIteratorIterator(
        new ParentIterator(new RecursiveArrayIterator($haystack)),
        RecursiveIteratorIterator::SELF_FIRST
    );

    foreach ($it as $key => $value) {
        if (array_key_exists('id', $value) && strcasecmp($value['id'], $needle) === 0) {
            $path = array($needle);
            for ($i = $it->getDepth() - 1; $i >= 0; $i--) {
                $path = array($it->getSubIterator($i)->key() => $path);
            }
            break;
        }
    }

    return $path;
}

Reference

  • RecursiveIteratorIterator::SELF_FIRST iteration mode - required to see non-"leaf" items
  • ParentIterator - easy way to filter out the "leaf" items
  • RecursiveIteratorIterator::getDepth()
  • RecursiveIteratorIterator::getSubIterator()

Bonus

You could also use the RecursiveIteratorIterator::setMaxDepth() method to limit the recursion to n levels deep, if your array also goes much deeper.



来源:https://stackoverflow.com/questions/13629214/get-all-parent-nodes-with-recursivearrayiterator

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!