Php 如何在使用递归ArrayIterator时更改数组键和值?

Php 如何在使用递归ArrayIterator时更改数组键和值?,php,iterator,spl,arrayiterator,Php,Iterator,Spl,Arrayiterator,我怀疑我在这里做了一些愚蠢的事情,但我对SPL的一个看似简单的问题感到困惑: 如何使用/,修改数组的内容(本例中的值) 使用下面的测试代码,我可以使用and更改循环中的值,并在循环中转储修改后的数组 但当我离开循环并从迭代器中转储数组时,它会返回到原始值。发生了什么事 $aNestedArray = array(); $aNestedArray[101] = range(100, 1000, 100); $aNestedArray[201] = range(300, 25, -25); $aNe

我怀疑我在这里做了一些愚蠢的事情,但我对SPL的一个看似简单的问题感到困惑:

如何使用/,修改数组的内容(本例中的值)

使用下面的测试代码,我可以使用and更改循环中的值,并在循环中转储修改后的数组

但当我离开循环并从迭代器中转储数组时,它会返回到原始值。发生了什么事

$aNestedArray = array();
$aNestedArray[101] = range(100, 1000, 100);
$aNestedArray[201] = range(300, 25, -25);
$aNestedArray[301] = range(500, 0, -50);

$cArray = new ArrayObject($aNestedArray);
$cRecursiveIter = new RecursiveIteratorIterator(new RecursiveArrayIterator($cArray), RecursiveIteratorIterator::LEAVES_ONLY);

// Zero any array elements under 200  
while ($cRecursiveIter->valid())
{
    if ($cRecursiveIter->current() < 200)
    {
        $cInnerIter = $cRecursiveIter->getInnerIterator();
        // $cInnerIter is a RecursiveArrayIterator
        $cInnerIter->offsetSet($cInnerIter->key(), 0);
    }

    // This returns the modified array as expected, with elements progressively being zeroed
    print_r($cRecursiveIter->getArrayCopy());

    $cRecursiveIter->next();
}

$aNestedArray = $cRecursiveIter->getArrayCopy();

// But this returns the original array.  Eh??
print_r($aNestedArray);
$aNestedArray=array();
$aNestedArray[101]=范围(1001000100);
$aNestedArray[201]=范围(300,25,-25);
$aNestedArray[301]=范围(500,0,-50);
$cArray=新的ArrayObject($aNestedArray);
$cRecursiveIter=new recursiveiterator(new RecursiveArrayIterator($cArray),recursiveiterator::LEAVES_ONLY);
//将200以下的任何数组元素归零
而($cRecursiveIter->valid())
{
如果($cRecursiveIter->current()<200)
{
$cInnerIter=$cRecursiveIter->getInnerIterator();
//$cInnerIter是一个递归数组迭代器
$cInnerIter->offsetSet($cInnerIter->key(),0);
}
//这将按预期返回修改后的数组,元素逐渐归零
打印($cRecursiveIter->getArrayCopy());
$cRecursiveIter->next();
}
$aNestedArray=$cRecursiveIter->getArrayCopy();
//但这将返回原始数组。嗯??
打印(数组);

看起来像getInnerIterator创建了子迭代器的副本

也许有不同的方法?(请继续关注)


更新:在对它进行了一段时间的黑客攻击之后,并引入了其他3名工程师,看起来PHP并没有为您提供改变子迭代器值的方法

您始终可以使用旧的备用:

<?php  
// Easy to read, if you don't mind references (and runs 3x slower in my tests) 
foreach($aNestedArray as &$subArray) {
    foreach($subArray as &$val) {
       if ($val < 200) {
            $val = 0;
        }
    }
}
?>

我知道这并不能直接回答您的问题,但在迭代对象时修改该对象并不是一种好的做法。

它可以归结为通过引用传递还是通过值传递

例如,尝试更改:

$cArray = new ArrayObject($aNestedArray);
致:


普通数组中的值似乎不可修改,因为它们不能通过引用传递给
ArrayIterator
的构造函数(
RecursiveArrayIterator
从此类继承其
offset*()
方法,请参阅)。因此,对
offsetSet()
的所有调用都在数组的副本上工作

我想他们之所以选择避免引用调用,是因为在面向对象的环境中(例如,当传递
ArrayObject
的实例时,这应该是默认情况),引用没有多大意义

还有一些代码可以说明这一点:

$a = array();

// Values inside of ArrayObject instances will be changed correctly, values
// inside of plain arrays won't
$a[] = array(new ArrayObject(range(100, 200, 100)),
             new ArrayObject(range(200, 100, -100)),
             range(100, 200, 100));
$a[] = new ArrayObject(range(225, 75, -75));

// The array has to be
//     - converted to an ArrayObject or
//     - returned via $it->getArrayCopy()
// in order for this field to get handled properly
$a[] = 199;

// These values won't be modified in any case
$a[] = range(100, 200, 50);

// Comment this line for testing
$a = new ArrayObject($a);

$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($a));

foreach ($it as $k => $v) {
    // getDepth() returns the current iterator nesting level
    echo $it->getDepth() . ': ' . $it->current();

    if ($v < 200) {
        echo "\ttrue";

        // This line is equal to:
        //     $it->getSubIterator($it->getDepth())->offsetSet($k, 0);
        $it->getInnerIterator()->offsetSet($k, 0);
    }

    echo ($it->current() == 0) ? "\tchanged" : '';
    echo "\n";
}

// In this context, there's no real point in using getArrayCopy() as it only
// copies the topmost nesting level. It should be more obvious to work with $a
// itself
print_r($a);
//print_r($it->getArrayCopy());
$a=array();
//将正确更改ArrayObject实例中的值,包括
//普通数组的内部不会
$a[]=数组(新的ArrayObject(范围(100200100)),
新的ArrayObject(范围(200100,-100)),
范围(100200100));
$a[]=新阵列对象(范围(225,75,-75));
//数组必须是
//-转换为ArrayObject或
//-通过$it->getArrayCopy()返回
//为了正确处理此字段
$a[]=199;
//在任何情况下都不会修改这些值
$a[]=范围(100、200、50);
//注释此行以进行测试
$a=新阵列对象($a);
$it=新递归迭代器(新递归数组迭代器($a));
foreach($k=>v){
//getDepth()返回当前迭代器嵌套级别
echo$it->getDepth().:'.$it->current();
如果($v<200){
回声“\t真”;
//这条线等于:
//$it->GetSub迭代器($it->getDepth())->偏移集($k,0);
$it->getInnerIterator()->offsetSet($k,0);
}
echo($it->current()==0)?“\t已更改”:”;
回音“\n”;
}
//在这种情况下,使用getArrayCopy()没有实际意义,因为它只是
//复制最顶层的嵌套级别。使用$a应该更为明显
//本身
印刷费($a);

//打印($it->getArrayCopy())
不使用迭代器类(它似乎在
递归ArrayIterator::beginChildren()
上复制数据,而不是通过引用传递。)

你可以使用以下方法来实现你想要的

function drop_200(&$v) { if($v < 200) { $v = 0; } }

$aNestedArray = array();
$aNestedArray[101] = range(100, 1000, 100);
$aNestedArray[201] = range(300, 25, -25);
$aNestedArray[301] = range(500, 0, -50);

array_walk_recursive ($aNestedArray, 'drop_200');

print_r($aNestedArray);
函数drop_200(&$v){if($v<200){$v=0;}
$aNestedArray=array();
$aNestedArray[101]=范围(1001000100);
$aNestedArray[201]=范围(300,25,-25);
$aNestedArray[301]=范围(500,0,-50);
数组_walk_recursive($aNestedArray,'drop_200');
打印(数组);

或者使用
create_function()
而不是创建drop_200函数,但是您的里程数可能会随着create_函数和内存使用的不同而变化。

您需要在当前深度调用
getSubIterator
,在该深度使用
offsetSet
,并对返回树的所有深度执行相同的操作

这对于在数组或数组中的值上进行无限制级别的数组合并和替换非常有用。不幸的是,
array\u walk\u recursive
在这种情况下不起作用,因为该函数只访问叶节点。。因此,下面$array中的“replace_this_array”键将永远不会被访问

例如,要替换数组中的所有值(但仅替换包含某个键的值),请执行以下操作:

$array = [
    'test' => 'value',
    'level_one' => [
        'level_two' => [
            'level_three' => [
                'replace_this_array' => [
                    'special_key' => 'replacement_value',
                    'key_one' => 'testing',
                    'key_two' => 'value',
                    'four' => 'another value'
                ]
            ],
            'ordinary_key' => 'value'
        ]
    ]
];

$arrayIterator = new \RecursiveArrayIterator($array);
$completeIterator = new \RecursiveIteratorIterator($arrayIterator, \RecursiveIteratorIterator::SELF_FIRST);

foreach ($completeIterator as $key => $value) {
    if (is_array($value) && array_key_exists('special_key', $value)) {
        // Here we replace ALL keys with the same value from 'special_key'
        $replaced = array_fill(0, count($value), $value['special_key']);
        $value = array_combine(array_keys($value), $replaced);
        // Add a new key?
        $value['new_key'] = 'new value';

        // Get the current depth and traverse back up the tree, saving the modifications
        $currentDepth = $completeIterator->getDepth();
        for ($subDepth = $currentDepth; $subDepth >= 0; $subDepth--) {
            // Get the current level iterator
            $subIterator = $completeIterator->getSubIterator($subDepth); 
            // If we are on the level we want to change, use the replacements ($value) other wise set the key to the parent iterators value
            $subIterator->offsetSet($subIterator->key(), ($subDepth === $currentDepth ? $value : $completeIterator->getSubIterator(($subDepth+1))->getArrayCopy()));
        }
    }
}
return $completeIterator->getArrayCopy();
// return:
$array = [
    'test' => 'value',
    'level_one' => [
        'level_two' => [
            'level_three' => [
                'replace_this_array' => [
                    'special_key' => 'replacement_value',
                    'key_one' => 'replacement_value',
                    'key_two' => 'replacement_value',
                    'four' => 'replacement_value',
                    'new_key' => 'new value'
                ]
            ],
            'ordinary_key' => 'value'
        ]
    ]
];

首先将数组转换为对象,它将按预期工作

    $array = [
        'one' => 'One',
        'two' => 'Two',
        'three' => [
            'four' => 'Four',
            'five' => [
                'six' => 'Six',
                'seven' => 'Seven'
            ]
        ]
    ];

    // Convert to object (using whatever method you want)
    $array = json_decode(json_encode($array));

    $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
    foreach($iterator as $key => $value) {
        $iterator->getInnerIterator()->offsetSet($key, strtoupper($value));
    }

    var_dump($iterator->getArrayCopy());

我认为这不像getInnerIterator创建副本那么简单,因为循环中的
$cRecursiveIter->getArrayCopy()
会给出修改后的值。你确定吗?
RecursiveArrayIterator
docs说“此迭代器允许在对数组和对象进行迭代时取消设置和修改值和键…”。不,这没有帮助。值得一提的是,迭代器也不允许使用引用foreach值-也就是说,这会导致一个致命错误:
foreach($cRecursiveIter as&$iVal)
看起来这是一个你应该用那些该死的SPL迭代器c提交的错误,浪费了一个小时
$array = [
    'test' => 'value',
    'level_one' => [
        'level_two' => [
            'level_three' => [
                'replace_this_array' => [
                    'special_key' => 'replacement_value',
                    'key_one' => 'testing',
                    'key_two' => 'value',
                    'four' => 'another value'
                ]
            ],
            'ordinary_key' => 'value'
        ]
    ]
];

$arrayIterator = new \RecursiveArrayIterator($array);
$completeIterator = new \RecursiveIteratorIterator($arrayIterator, \RecursiveIteratorIterator::SELF_FIRST);

foreach ($completeIterator as $key => $value) {
    if (is_array($value) && array_key_exists('special_key', $value)) {
        // Here we replace ALL keys with the same value from 'special_key'
        $replaced = array_fill(0, count($value), $value['special_key']);
        $value = array_combine(array_keys($value), $replaced);
        // Add a new key?
        $value['new_key'] = 'new value';

        // Get the current depth and traverse back up the tree, saving the modifications
        $currentDepth = $completeIterator->getDepth();
        for ($subDepth = $currentDepth; $subDepth >= 0; $subDepth--) {
            // Get the current level iterator
            $subIterator = $completeIterator->getSubIterator($subDepth); 
            // If we are on the level we want to change, use the replacements ($value) other wise set the key to the parent iterators value
            $subIterator->offsetSet($subIterator->key(), ($subDepth === $currentDepth ? $value : $completeIterator->getSubIterator(($subDepth+1))->getArrayCopy()));
        }
    }
}
return $completeIterator->getArrayCopy();
// return:
$array = [
    'test' => 'value',
    'level_one' => [
        'level_two' => [
            'level_three' => [
                'replace_this_array' => [
                    'special_key' => 'replacement_value',
                    'key_one' => 'replacement_value',
                    'key_two' => 'replacement_value',
                    'four' => 'replacement_value',
                    'new_key' => 'new value'
                ]
            ],
            'ordinary_key' => 'value'
        ]
    ]
];
    $array = [
        'one' => 'One',
        'two' => 'Two',
        'three' => [
            'four' => 'Four',
            'five' => [
                'six' => 'Six',
                'seven' => 'Seven'
            ]
        ]
    ];

    // Convert to object (using whatever method you want)
    $array = json_decode(json_encode($array));

    $iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
    foreach($iterator as $key => $value) {
        $iterator->getInnerIterator()->offsetSet($key, strtoupper($value));
    }

    var_dump($iterator->getArrayCopy());