简体   繁体   中英

How to modify certain Array keys with RecursiveIteratorIterator

I have a multi-level Array. Number of levels (sub-arrays) isn't known. It could be only two, but also could be 5. The only thing I know - there are certain (known) keys on each level.

Let consider following example:

Array
(
    [number] => 21
    [otherkey] => value
    [sub] => Array
        (
            [count] => 
            [number] => 29
            [some] => thing
            [range] => Array
                (
                   ...
                )

            [sub] => Array
                (
                    [count] => 1
                    [number] => 16
                    [key] => value
                    [date] => 2013-07-25
                    [sub] => Array
                        (
                            [count] => 0
                            [number] => 4
                            [key] => value
                            [sub] => Array
                                (
                                    [count] => 1
                                    [number] => 24
                                    [key] => value
                                    [sub] => last
                                )

                        )

                )

        )

)

My goal is to iterate through all 'number' keys and create text like 'even' or 'odd', but the result should be saved under own key (say number_str ) on each level

So modified Array should look like this:

 Array
    (
        [number] => 21
        [otherkey] => value
        [sub] => Array
            (
                [count] => 
                [number] => 29
                [some] => thing
                [range] => Array
                    (
                     ...
                    )
                [sub] => Array
                    (
                        [count] => 1
                        [number] => 16
                        [key] => value
                        [date] => 2013-07-25
                        [sub] => Array
                            (
                                [count] => 0
                                [number] => 4
                                [key] => value
                                [sub] => Array
                                    (
                                        [count] => 1
                                        [number] => 24
                                        [key] => value
                                        [sub] => last
                                        [number_str] => even //new key
                                    )
                                [number_str] => even //new key

                            )
                        [number_str] => even //new key

                    )
                [number_str] => odd //new key

            )
        [number_str] => odd //new key
    )

So I tried to use RecursiveIteratorIterator

$rai = new RecursiveArrayIterator($data);
$rii = new RecursiveIteratorIterator($rai);

foreach ($rii as $idx => $level) {
    if($idx === 'number')
    {
        $str = ($level % 2) ? 'odd' : 'even';
        $rii->offsetSet('number_str', $str);
    }
}

but it didn't worked. So I found kind of 'workaround'. It seems to do, what I expect if each Sub-Array is an Object of stdClass

convert the original Array (and all sub-array) to a stdClass

$data = json_encode(json_decode($data)); // ..dirty gimmick. Possible performance killer ?

$rai = new RecursiveArrayIterator($data);
$rii = new RecursiveIteratorIterator($rai);

foreach ($rii as $idx => $level) {
    //same logic as above...
}

var_dump($rai);

now it looks like I have wished results, the only problem is, all sub-level now are instances of stdClass

code online -> http://ideone.com/PIiZOX

QUESTION Is there a way to get the same results, but without the gimmick with json_decode / json_encode so the output will be a normal Array ?

PS : I know, there is array_walk() but it's not an option

UPDATE: I should mention that the question is not about getting correct results. I know, that custom recursiv function (and maybe array_walk_recursive ()) will do the trick. Its more about SPLs RecursiveIterators as possible solutuion. I just wonder myself, why it does work with strClass es and why don't with regular Array.

The example Array from obeve isn't real use-case. As you may guess, the real use-case are Arrays (sometimes more than 600 rows) with data from DB. Some of them have sub-arrays. The only thing i know are keys which should be modified (by a given rule). Performance is a main criteria

Try with a custom recursive function:

function addEvenOrOdd(&$array) {
    $array['number_str'] = ($array['number'] % 2) ? 'odd' : 'even';
    ksort($array);
    if(isset($array['sub']) && is_array($array['sub'])) {
        addEvenOrOdd($array['sub']);
    }
}
addEvenOrOdd($yourarray);

See it working live here: http://codepad.viper-7.com/vBc42b

I think it's not need to use RecursiveArrayIterator, simple recursive function will be enough. Hope this will helps:

function iterate_recursive( &$target_arr ){
    $val = 'odd';
    if( $target_arr[ 'number' ] % 2 == 0 ){
        $val = 'even';
    }
    $target_arr[ 'number_str' ] = $val;

    if( array_key_exists( 'sub', $target_arr ) and is_array($target_arr['sub'])){
        iterate_recursive( $target_arr[ 'sub' ] );
    }
}

$dta_arr = Array
    (
    'number' => 21,
    'otherkey' => 'value',
    'sub' => Array
        (
        'count' => 123,
        'number' => 29,
        'some' => 'thing',
        'range' => Array
            (
            'stub' => False,
        ),
        'sub' => Array
            (
            'count' => 1,
            'number' => 16,
            'key' => 'value',
            'date' => 2013 - 07 - 25,
            'sub' => Array
                (
                'count' => 0,
                'number' => 4,
                'key' => 'value',
                'sub' => Array
                    (
                    'count' => 1,
                    'number' => 24,
                    'key' => 'value',
                    'sub' => 'thing',
                )
            ),
        ),
    ),
);


iterate_recursive( $dta_arr );
print_r($dta_arr);
function even_or_odd(&$arr){
    if(is_array($arr)){
        if (isset($arr['number'])){
            if ($arr['number']%2 == 1){
                $arr['number_str'] = 'odd';
            } else {
                $arr['number_str'] = 'even';
            }
        }
        foreach( $arr as $key => &$val ){
            if(is_array($val)){
                self::even_or_odd($val);
            }
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM