使用PHP迭代器为' array_walk_recursive()'实现相同功能的正确方法是什么?功能?

时间:2014-07-15 18:07:43

标签: php arrays iterator

我需要递归遍历所有数组元素(即,对于遇到的每个数组元素本身就是一个数组,我需要迭代该数组)并对每个字符串(叶子)值执行trim()

要使用与下面链接中提到的示例非常相似的示例,这就是我需要的:

<?php


    // How to accomplish identical functionality using PHP iterators?


    $sweet = array('a' => ' apple ', 'b' => ' banana ');
    $fruits = array('sweet' => $sweet, 'sour' => ' lemon ');

    function my_trim(&$item, $key)
    {
        // Trim the whitespace from the values
        $item = trim($item);
    }

    array_walk_recursive($fruits, 'my_trim');

?>

函数array_walk_recursive提供了我所需的功能。

但是,我想更好地理解PHP迭代器,并且通过使用迭代器实现与array_walk_recursive()函数相同的功能,我当前的任务似乎是一个很好的机会。

当我转到PHP documentation for iterators时,我看到了各种迭代器,但没有对它们语义的整体描述。特别是,RecursiveArrayIterator似乎是明显的选择,如果不是RecursiveIteratorIterator类和this StackOverflow answer类似的问题(它给出了代码但不解释代码)令我满意的是,我只是试图使用前迭代器(RecursiveArrayIterator),我甚至可能都没有发布过这个问题。

但是,我尝试实际查看两个相关迭代器类的文档 - RecursiveArrayIteratorRecursiveIteratorIterator - 以便了解上述链接中的代码并且对它的使用感到满意(或没有) - 我在Google搜索中的任何地方都没有找到这些迭代器语义的文档(特别是,我在官方PHP文档中没有看到这些迭代器的介绍性文档,如上所述)。

因此,我想知道使用PHP迭代器实现array_walk_recursive()函数的相同功能的正确方法是什么。 (据推测,如果上面的SO链接中的代码是正确的,它将与链接代码完全(或类似) - 但我不仅仅是代码片段,还有(简要)解释。)

2 个答案:

答案 0 :(得分:1)

我不知道使用RecursiveIteratorIterator修改原始数组的简单方法,而是使用array_walk_recursive,因为您的示例是更好的选择。

如果您只想访问多维数组的叶子而不修改原始数组,可以使用LEAVES_ONLY选项。

$sweet = array('a' => ' apple ', 'b' => ' banana ');
$fruits = array('sweet' => $sweet, 'sour' => ' lemon ');

foreach (new RecursiveIteratorIterator(
             new RecursiveArrayIterator($fruits),
             RecursiveIteratorIterator::LEAVES_ONLY
         ) as $key => $value) {
    print trim($value);
}

请注意,在上面的foreach中引用$ value即as $key => &$value是非法的,在这种情况下php会产生以下错误

PHP Fatal error:  An iterator cannot be used with foreach by reference

RecursiveIteratorIterator以递归方式迭代任何实现Traversable(和扩展名Iterator)接口的递归对象。对于嵌套数组,您需要一个RecursiveArrayIterator迭代器对象 但您也可以RecursiveDirectoryIterator遍历文件系统。

您遍历对象的路径可以由RecursiveIteratorIterator的第二个参数指定:

RecursiveIteratorIterator::LEAVES_ONLY - The default. Lists only leaves in iteration.
RecursiveIteratorIterator::SELF_FIRST - Lists leaves and parents in iteration with parents coming first.
RecursiveIteratorIterator::CHILD_FIRST - Lists leaves and parents in iteration with leaves coming first.

答案 1 :(得分:1)

Iterator并不意味着修改它的内部元素。 Iterator用于在foreach循环中逐个访问元素集合。

当然,您可以以返回字符串的修改版本的方式覆盖Iterator的current()函数。但是,您仍然需要自己迭代所有元素来修改它们。

我试着通过例子解释:

$sweet = array('a' => ' apple ', 'b' => ' banana ');
$fruits = array('sweet' => $sweet, 'sour' => ' lemon ');

创建自定义RecursiveIteratorIterator

class CallbackRecursiveIteratorIterator extends
    RecursiveIteratorIterator
{

    public function __construct(RecursiveIterator $it, $callback) {
        $this->callback = $callback;
        parent::__construct($it);
    }   

    public function current() {
        return $this->callback->__invoke(parent::current());
    }   

}

创建该迭代器的实例:

$i = new CallbackRecursiveIteratorIterator (
    new RecursiveArrayIterator(
            $fruits
    ),  
    // pass callback here
    function($element) {
        return trim($element . " test");
    }   
);


// Iterate over modified values
foreach($i as $key => $el) {
    var_dump($el);
}

请注意,原始数组仍未更改:

var_dump($fruits);