1

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 ?

P.S : 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 strClasses 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

3 Answers 3

1
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);
            }
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

1

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

2 Comments

Take a look here: php.net/manual/en/class.recursiveiteratoriterator.php Here is some benchmark in comments, comparing iterations and simple recursive function realization, if speed is most important for you, when recursive functions are more quick than iterators.
Also, by default RecursiveIteratorIterator returns flattered array, and if nesting levels order is important for you, when you will need to use $iterator->getArrayCopy() method, what will return multilevel array, but will double amount of memory used to store both arrays, and also will add time delay to proceed it

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.