How do I alter array keys and values while using a RecursiveArrayIterator? How do I alter array keys and values while using a RecursiveArrayIterator? php php

How do I alter array keys and values while using a RecursiveArrayIterator?


It seems that values in plain arrays aren't modifiable because they can't be passed by reference to the constructor of ArrayIterator (RecursiveArrayIterator inherits its offset*() methods from this class, see SPL Reference). So all calls to offsetSet() work on a copy of the array.

I guess they chose to avoid call-by-reference because it doesn't make much sense in an object-oriented environment (i. e. when passing instances of ArrayObject which should be the default case).

Some more code to illustrate this:

$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// itselfprint_r($a);//print_r($it->getArrayCopy());


Not using the Iterator classes (which seem to be copying data on the RecursiveArrayIterator::beginChildren() instead of passing by reference.)

You can use the following to achieve what you want

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

or use create_function() instead of creating the drop_200 function, but your mileage may vary with the create_function and memory usage.


You need to call getSubIterator at the current depth, use offsetSet at that depth, and do the same for all depths going back up the tree.

This is really useful for doing unlimited level array merge and replacements, on arrays or values within arrays. Unfortunately, array_walk_recursive will NOT work in this case as that function only visits leaf nodes.. so the 'replace_this_array' key in $array below will never be visited.

As an example, to replace all values within an array unknown levels deep, but only those that contain a certain key, you would do the following:

$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'        ]    ]];