【问题标题】:php - deep search array of arrays and return only matching elementsphp - 深度搜索数组并仅返回匹配的元素
【发布时间】:2017-12-01 22:14:10
【问题描述】:

我正在寻找php 中的解决方案,如该问题的已接受答案中所述:

javascript - return parent with only child that matches given search string in array of objects with nested object

请查看以下代码:

<?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();
    foreach ($stack as $k => $v) {
        if (is_array($v)) {
            // If the current element of the array is an array, recurse it 
            // and capture the return stack
            $stack = getParentStackComplete($child, $v);

            // If the return stack is an array, add it to the return
            if (is_array($stack) && !empty($stack)) {
                $return[] = $v;
            }
        } else {
            // Since we are not on an array, compare directly
            if(strpos($v, $child) !== false){               
                // And if we match, stack it and return it
                $return[] = $v;
            }
        }
    }

    // Return the stack
    return empty($return) ? false: $return;
}


echo "<pre>";
print_r(getParentStackComplete('Oracle', $items['tableData']));
echo "</pre>";


?>

此代码运行良好。我在网上找到了函数getParentStackComplete,修改为返回整个匹配元素。它递归地搜索数组并返回匹配项。

例如,如代码中给出的,如果我搜索字符串“Oracle”,它应该返回一个数组,其中包含一个在asset_info 中只有一个子项(匹配元素)的项目。我正在寻找的输出是:

Array
(
    [0] => 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
        )

)

如果我搜索字符串HP BL460C GEN8,它应该返回如下:

Array
(
    [0] => 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
        )
       [1] => 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'
       )

)

如何在嵌套数组搜索中返回匹配的子级和父级?

【问题讨论】:

    标签: php arrays


    【解决方案1】:

    试试这个代码。

    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;
    }
    

    【讨论】:

    • 这是完美的解决方案。奇迹般有效。非常感谢。
    【解决方案2】:

    要仅深入搜索叶节点,这很简单,通过RecursiveIterator 进行递归迭代,其RecursiveIteratorIterator 通过RecursiveArrayIterator 处理仅叶遍历。

    为了在此处显示您的示例数据的小型搜索示例:

    $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);
    }
    

    确实

    1. 将要搜索的数组装饰为 RecursiveArrayIterator
    2. 通过RecursiveIteratorIterator装饰数组迭代器以实现仅叶遍历。
    3. 对所有叶值应用搜索(再次作为装饰器)。
    4. 剩下的就是到foreach搜索并输出值进行演示了。

    并且会输出:

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

    搜索是在三行代码中有效设置的。

    这还不是全部,因为在 foreach 中我们仍然有修饰迭代的上下文,您不仅可以访问当前值,还可以访问上三个级别,你想要返回:

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

    这将输出匹配搜索的所有父对象。

    可能已经回答了你的问题,所以让我们完整地展示这个例子:

    $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);
    }
    

    它的输出:

    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
    )
    

    但是,如果您可能想减少父母 asset_info 数组以仅包含那些匹配项,而不仅仅是包含匹配项的所有父母项。如果是这样,则需要创建一个结果数组,该数组仅包含 asset_info 条目中匹配的那些条目。这需要跟踪匹配的父母,以便将匹配的asset_infos 添加到他们的结果中。

    因为这需要处理同一父级的所有匹配资产,然后仅向该父级提供这些匹配项。因此匹配项将在其父项中分组,因此这是一种聚合函数,因此需要管理更多的事情,因为它需要跟踪父项是否已经拥有所有匹配项:

    $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);
    }
    

    输出在您的情况下给出:

    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
    )
    

    注意第一个匹配项在asset_info 中的条目数的细微差别,即一个而不是前两个。

    【讨论】:

    • 感谢您的回复。是的,这很好用。但是,如果我搜索不在数组内的值,则该函数不会返回任何内容。例如,如果我搜索“保证”或“enm”。仅供参考-我之前没有使用过RecursiveArrayIterator
    • 我无法使用此解决方案仅在父级中搜索。
    • 您可以通过使用堆栈的搜索过滤器来做到这一点,如获取父级的位置所示。遍历和搜索可以完全定制,我确实读过你想要搜索叶子的问题,所以我证明了这一点。
    【解决方案3】:
    <?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>";
    

    【讨论】:

    • 为什么要删除递归函数调用?
    • 嗨,Valay,这是另一种使用递归函数获得结果的替代方法,我们将无法获得结果。如果我们评论递归函数以获得正确的输出,这并没有错......
    • 你好 Valay 我在想我们需要第二个数组但我错了,你应该试试这个代码,它会给你正确的数据......你需要使用 unset($v['asset_info'] [1]);在循环中...查看并检查...
    • 请再试一次更新后的代码,如果您遇到任何问题,请告诉我?
    【解决方案4】:

    我希望返回基于完全匹​​配的结果,因此使用第三个参数 strict 在下面修改了 Manish 的答案。传递 true 将仅返回完全匹配。

    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;
    }
    

    【讨论】:

      【解决方案5】:

      这应该可以解决问题。

      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;
      }
      

      编辑:假设您像这样调用getParentStackComplete('Oracle', $items['tableData'])

      【讨论】:

      • 感谢您的回复。但是asset_info 嵌套数组不是强制性的。并且无论键名如何,解决方案都应该是动态的。
      • 您所说的动态而与键名无关是什么意思?你能举个例子吗?如果您只是意味着您需要检查其他键,我的解决方案很容易扩展为不进行硬编码。如果您需要树的深度是动态的,那么您需要递归地遍历树。至于不需要“asset_info”密钥,只需在继续之前检查它是否存在。这不是一个供人们为您编写所有代码的网站,它是为了帮助您找到解决方案。
      • 我想我知道 SO 是如何工作的,而且我已经尝试过我所知道的一切。感谢您提供的解决方案,并让我知道在 SO 上该做什么以及如何做。
      猜你喜欢
      • 2016-10-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-22
      • 1970-01-01
      • 2012-10-25
      • 2021-09-12
      相关资源
      最近更新 更多