php - deep search array of arrays and return only matching elements

后端 未结 5 921
忘掉有多难
忘掉有多难 2020-12-18 16:31

I am looking for a solution in php as mentioned in the accepted answer of this question:

javascript - return parent with only child that matches given s

相关标签:
5条回答
  • 2020-12-18 16:42

    Try this code.

    function getParentStackComplete( $search, $stack ){
    
        $results = array();
    
        foreach( $stack as $item ){
    
            if( is_array( $item ) ){
    
                if( array_filter($item, function($var) use ($search) { return ( !is_array( $var ) )? stristr( $var, $search ): false; } ) ){
                    //echo 'test';
                    $results[] = $item;
                    continue;
                }else if( array_key_exists('asset_info', $item) ){
                    $find_assets = array();
                    foreach( $item['asset_info'] as $k=>$v ){
                        //echo 'abc ';
    
                        if( is_array( $v ) && array_filter($v, function($var) use ($search) { return stristr($var, $search); }) ){
                            $find_assets[] = $v;
                        }
                    }
                    if( count( $find_assets ) ){
                        $temp = $item;
                        $temp['asset_info'] = $find_assets;
                        $results[] = $temp;
                    }
                }
            }
        }
    
        return $results;
    }
    
    0 讨论(0)
  • 2020-12-18 16:45
    <?php
        $items = array( 
            'tableData' => array
            (
                array
                (
                    'booking_name' => 'abc/xyz/123',
                    'pdg' => 'assure',                    
                    'user_area' => 'es st1',
                    'release' => 'oss72',
                    'start_date' => '2017-06-20 00:00:00',
                    'end_date' => '2017-06-23 00:00:00',
                    'asset_info' => array
                        (
                            array
                                (
                                    'status' => 10,
                                    'manufacturer' => 'Oracle',
                                    'model' => 'HP BL460C GEN8',
                                    'hardware_color' => '#0066b3',
                                ),
                            array
                                (
                                    'status' => 11,
                                    'manufacturer' => 'HP',
                                    'model' => 'HP BL460C GEN81',
                                    'hardware_color' => '#0066b3',
                                )
    
                        ),
    
                    'full_name' => 'Valay Desai',
                    'email_address' => 'valay@xyz.com',
                ),
    
                array
                (
                    'booking_name' => 'abc/xyz/123',
                    'pdg' => 'enm',                    
                    'user_area' => 'es st',
                    'release' => 'oss72',
                    'start_date' => '2017-06-20 00:00:00',
                    'end_date' => '2017-06-23 00:00:00',
                    'asset_info' => array
                        (
                            array
                                (
                                    'status' => 10,
                                    'manufacturer' => 'HP',
                                    'model' => 'HP BL460C GEN8',
                                    'hardware_color' => '#0066b3',
                                )
    
                        ),
    
                    'full_name' => 'Valay Desai',
                    'email_address' => 'valay@xyz.com',
                )
            )
        );
    
    function getParentStackComplete($child, $stack) {
        $return = array();
        $k=0;   
        foreach ($stack as $k => $v) { 
    
            if (is_array($v)) {          
    
    
                if (is_array($stack) && !empty($stack) && $k==0) {
                    unset($v['asset_info'][1]);
                    $return = $v;              
    
                }
            } else {
    
                if(strpos($v, $child) !== false){    
    
                    $return[] = $v;
                }
            }
            $k++;
        }    
        return empty($return) ? false: $return;
    }
    
    
    echo "<pre>";
    print_r(getParentStackComplete('Oracle', $items['tableData']));
    echo "</pre>";
    
    0 讨论(0)
  • 2020-12-18 16:55

    I was looking to return a result based on an exact match so have modified Manish's answer below with a third parameter of strict. Passing true will return an exact match only.

    public function getParentStackComplete($search, $stack, $strict = false)
    {
        $results = array();
    
        foreach ($stack as $item) {
    
            if (is_array($item)) {
    
                if (array_filter($item, function ($var) use ($search) {
                    return (!is_array($var)) ? stristr($var, $search) : false;
                })) {
                    //echo 'test';
                    $results[] = $item;
    
                    if ($strict) {
                        if ($item['name'] === $search)
                            return $item;
                    }
    
                    continue;
                } else if (array_key_exists('asset_info', $item)) {
                    $find_assets = array();
                    foreach ($item['asset_info'] as $k => $v) {
                        //echo 'abc ';
    
                        if (is_array($v) && array_filter($v, function ($var) use ($search) {
                                return stristr($var, $search);
                            })) {
                            $find_assets[] = $v;
                        }
                    }
                    if (count($find_assets)) {
                        $temp = $item;
                        $temp['asset_info'] = $find_assets;
                        $results[] = $temp;
                    }
    
                }
            }
        }
    
        return $results;
    }
    
    0 讨论(0)
  • 2020-12-18 17:04

    This should do the trick.

    function getParentStackComplete($search, $stack)
    {
        $results = [];
        foreach ($stack as $i => $item) {
            $results[] = $item;
            $cache = $item['asset_info'];
            $results[$i]['asset_info'] = [];
            $found = false;
    
            foreach ($cache as $asset) {
                if (array_search($search, $asset) !== false) {
                    print('here' . "\n");
                    $found = true;
                    $results[$i]['asset_info'][] = $asset;
                }
            }
    
            if (!$found) {
                unset($results[$i]);
            }
        }
        return $results;
    }
    

    Edit: This assumes you're calling like this getParentStackComplete('Oracle', $items['tableData'])

    0 讨论(0)
  • 2020-12-18 17:05

    To deeply search only the leaf nodes, this is straight forward with recursive iteration via RecursiveIterator, its RecursiveIteratorIterator handles leaf-only traversal via RecursiveArrayIterator.

    To make this visible here a small search example on your example data:

    $iterator = new RecursiveArrayIterator($items['tableData']); # 1.
    $leafs = new RecursiveIteratorIterator($iterator); # 2.
    $search = new RegexIterator($leafs, sprintf('~^%s$~', preg_quote('HP BL460C GEN8', '~'))); # 3.
    foreach ($search as $value) { # 4.
        var_dump($value);
    }
    

    It does

    1. Decorate the array to search through as a RecursiveArrayIterator.
    2. Decorate the array iterator for leafs-only traversal via RecursiveIteratorIterator.
    3. Apply the search (again as a decorator) on all the leaf values.
    4. The rest is to foreach the search and output the values for demonstration.

    And will output:

    string(14) "HP BL460C GEN8"
    string(14) "HP BL460C GEN8"
    

    The search is effectively set-up in three lines of code.

    And that's not all, as inside the foreach we still have the context of the decorated iteration, you can access not only the current value, but also that three levels up, the parent you want to return:

    foreach ($search as $key => $value) {
        $parentLevel = 0; # or for relative access: $leafs->getDepth() - 3
        $parent = $leafs->getSubIterator($parentLevel)->current();
        var_dump($parent);
    }
    

    This will output all parent objects that matched the search.

    That might answer your question already, so let's show the example in full:

    $search = function (array $array, string $term) {
        $iterator = new RecursiveArrayIterator($array);
        $leafs = new RecursiveIteratorIterator($iterator);
        $search = new RegexIterator($leafs, sprintf('~^%s$~', preg_quote($term, '~')));
        foreach ($search as $value) {
            $parent = $leafs->getSubIterator(0)->current();
            yield $parent;
        }
    };
    
    $matches = $search($items['tableData'], 'HP BL460C GEN8');
    foreach ($matches as $index => $match) {
        echo $index + 1, ': ';
        print_r($match);
    }
    

    And it's output:

    1: Array
    (
        [booking_name] => abc/xyz/123
        [pdg] => assure
        [user_area] => es st1
        [release] => oss72
        [start_date] => 2017-06-20 00:00:00
        [end_date] => 2017-06-23 00:00:00
        [asset_info] => Array
            (
                [0] => Array
                    (
                        [status] => 10
                        [manufacturer] => Oracle
                        [model] => HP BL460C GEN8
                        [hardware_color] => #0066b3
                    )
    
                [1] => Array
                    (
                        [status] => 11
                        [manufacturer] => HP
                        [model] => HP BL460C GEN81
                        [hardware_color] => #0066b3
                    )
    
            )
    
        [full_name] => Valay Desai
        [email_address] => valay@xyz.com
    )
    2: Array
    (
        [booking_name] => abc/xyz/123
        [pdg] => enm
        [user_area] => es st
        [release] => oss72
        [start_date] => 2017-06-20 00:00:00
        [end_date] => 2017-06-23 00:00:00
        [asset_info] => Array
            (
                [0] => Array
                    (
                        [status] => 10
                        [manufacturer] => HP
                        [model] => HP BL460C GEN8
                        [hardware_color] => #0066b3
                    )
    
            )
    
        [full_name] => Valay Desai
        [email_address] => valay@xyz.com
    )
    

    But what if you might want to reduce the parents asset_info array to only contain those matches and not only all parents containing a match. If so this would require to create a result array which only contains those entries in asset_info entries that were a match. This requires to keep track of matched parents so that matches asset_infos can be added to their result.

    As this requires to process all matched assets of the same parent and then provide that parent only with these matches. So matches will become grouped in their parent, therefore this is sort of an aggregation function and has therefore a bit more things to manage as it needs to keep track if the parent has all matches already:

    $search = function (array $array, string $term) {
        $iterator = new RecursiveArrayIterator($array);
        $leafs = new RecursiveIteratorIterator($iterator);
        /* @var $search RecursiveIteratorIterator|RegexIterator - $search is a decorator of that type */
        $search = new RegexIterator($leafs, sprintf('~^%s$~', preg_quote($term, '~')));
    
        # initialize
        $match = $lastId = null;
    
        foreach ($search as $key => $value) {
            $parentId = $search->getSubIterator(0)->key();
            if ($lastId !== $parentId && $match) {
                yield $match;
                $match = null;
            }
            $lastId = $parentId;
    
            if (empty($match)) {
                # set match w/o asset_info as yet not matched
                $match = $search->getSubIterator(0)->current();
                $match['asset_info'] = [];
            }
    
            # add matched asset into the matched asset_info
            $match['asset_info'][] = $search->getSubIterator(2)->current();
        }
    
        $match && yield $match;
    
    };
    
    $matches = $search($items['tableData'], 'HP BL460C GEN8');
    foreach ($matches as $index => $match) {
        echo $index + 1, ': ';
        print_r($match);
    }
    

    The output gives in your case:

    1: Array
    (
        [booking_name] => abc/xyz/123
        [pdg] => assure
        [user_area] => es st1
        [release] => oss72
        [start_date] => 2017-06-20 00:00:00
        [end_date] => 2017-06-23 00:00:00
        [asset_info] => Array
            (
                [0] => Array
                    (
                        [status] => 10
                        [manufacturer] => Oracle
                        [model] => HP BL460C GEN8
                        [hardware_color] => #0066b3
                    )
    
            )
    
        [full_name] => Valay Desai
        [email_address] => valay@xyz.com
    )
    2: Array
    (
        [booking_name] => abc/xyz/123
        [pdg] => enm
        [user_area] => es st
        [release] => oss72
        [start_date] => 2017-06-20 00:00:00
        [end_date] => 2017-06-23 00:00:00
        [asset_info] => Array
            (
                [0] => Array
                    (
                        [status] => 10
                        [manufacturer] => HP
                        [model] => HP BL460C GEN8
                        [hardware_color] => #0066b3
                    )
    
            )
    
        [full_name] => Valay Desai
        [email_address] => valay@xyz.com
    )
    

    Mind the subtle differences in the first match on the number of entries in the asset_info, that is one instead of the earlier two.

    0 讨论(0)
提交回复
热议问题