1

So my example inputs are

$example_1 = Array (
    0 => Array (
        'category'     => 'body',
        'sub-category' => 'intro',
        'id'           => 'header',
        'copy'         => 'Hello',
    ),
    1 => Array (
        'category'     => 'body',
        'sub-category' => 'intro',
        'id'           => 'footer',
        'copy'         => 'Bye',
    ),
);

$example_2 = Array (
    0 => Array (
        'category'     => 'body',
        'sub-category' => 'intro',
        'sub-sub-category' => 'header',
        'sub-sub-child-category' => 'left',
        'id'           => 'title',
        'copy'         => 'Hello',
    ),
    1 => Array (
        'category'     => 'body',
        'sub-category' => 'intro',
        'sub-sub-category' => 'footer',
        'sub-sub-child-category' => 'right',
        'id'           => 'title',
        'copy'         => 'Bye',
    ),
);

I want to transform it into

$example_output_1 = Array (
    'body' => Array (
        'intro' => Array (
            'header' => Array (
                'title' => 'Hello',
            ),
            'footer' => Array (
                'title' => 'Bye',
            ),
        ),
    ),
);

$example_output_2 = Array (
    'body' => Array (
        'intro' => Array (
            'header' => Array ( 
                'left' => Array (
                    'title' => 'Hello',
                ),
            ),
            'footer' => Array (
                'right' => Array (
                    'title' => 'Bye',
                )
            ),
        ),
    ),
);

Note the depth of the array is dynamic (it is not set - only by the time it hits 'copy' does it indicate the depth of the array).

I am having problems trying to get the recursion correctly. The basic but very rough algorithm I had was to - Loop through the Row - Loop through the contents of the Row - When the index is "copy" then the final value is current value. - Then build the array

I managed to get it to process for ONLY one row of the array but it was very messy and kinda patchy, so I got a feeling I really need to start from scratch. Updated: Attached embarrassing Code as requested (don't scream! ;p)

function buildArray($row, $start = true) {

    if ($start) {
        $result = array();
    }

    if ( ! is_array($row) ) {
        return $row;
    }

    // Get the first element of the array include its index
    $cellValue = null;
    $colId = null;
    foreach($row AS $index => $value) {
        $cellValue = $value;
        $colId = $index;
        break; 
    }

    // Reduce the array by one
    $tempRow = $row;
    $temp = array_shift($tempRow);

    if ($colId == 'copy') {
        $result[$cell] = buildArray($cellValue, $locale, false);
    } else {
      $result[$cell] = buildArray($tempRow, $locale, false);
    }

    return $result;
} 

Any help will be greatly appreciated.

3
  • 2
    Sounds pretty straight-forward. Can't you turn your word description of the algorithm into actual foreach loops? Otherwise, this kind of looks like a gimme teh codez request. We want to help you, not work for you ;) Give it another shot and post the result. Commented Jun 6, 2012 at 22:13
  • 2
    Thanks for posting the code. Don't fret. Always a good learning experience to share it with others and get feedback. Commented Jun 6, 2012 at 22:19
  • Most of the recursive nature can be solved by using array_merge_recursive, the rest is tail-recursive so can be solved iteratively :) Commented Jun 6, 2012 at 22:50

2 Answers 2

3

Should be pretty straightforward:

$originalArray = array(); // <-- should contain your values
$newArray = array();

foreach( $originalArray as $item )
{
    $category = $item['category'];
    $subcategory = $item['sub-category'];

    if ( empty( $newArray[$category] ) )
    {
        $newArray[$category] = array();
    }
    if ( empty( $newArray[$category][$subcategory] ) )
    {
        $newArray[$category][$subcategory] = array();
    }

    $newArray[$category][$subcategory][$item['id']] = $item['copy'];
}

See it here in action: http://codepad.viper-7.com/9bDiLP


Update: Now that you've specified that you need unlimited recursion, here's a shot at that:

$originalArray = array(); // <-- Your values go here
$newArray = array();

foreach ( $originalArray as $item )
{
    $inception = &$newArray; // http://www.imdb.com/title/tt1375666/

    foreach ( $item as $key => $val )
    {
        if ( $key != 'id' )
        {
            if ( empty($inception[$val]) )
            {
                $inception[$val] = array();
            }
            $inception = &$inception[$val];
        }
        else
        {
            $inception[ $val ] = $item['copy'];
            break;
        }
    }
}

...and here's the demo: http://codepad.viper-7.com/F9hY7h

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for that, I forgot to add that that is only one example of the input, the depth can actually be unknown (hence why I was trying to do recursion)
@MechaStorm - What would the deeper arrays hold? Can you add that to your question?
Thanks to all that have answered. It was a hard decision to choose which is the best acceptable answer as both are acceptable but StackOverflow won't allow >1 answer. I have chosen the other one as the acceptable answer as @Jack highlighted to me about array_merge_recursive function that I didn't realize existed. But I do also want to highlight this as also another acceptable answer.
1

This can be solved iteratively, because the recursion would only happen at the tail end of your function. The following code is the simplification. It builds a new layered array while it iterates over the old.

After transforming each each entry it gets merged using array_merge_recursive.

function transform($a)
{
    // create new array and keep a reference to it
    $b = array(); $cur = &$b;
    foreach ($a as $key => $value) {
        if ('id' === $key) {
            // we're done, add value to the array built so far using id and copy
            $cur[$value] = $a['copy'];
            break;
        } else {
            // create one more level
            $cur[$value] = array();
            // and update the reference
            $cur = &$cur[$value];
        }
    }
    // all done
    return $b;
}

// $example_2 is your multi-dimensional array
$merged = call_user_func_array('array_merge_recursive', 
    array_map('transform', $example_2)
);

1 Comment

Thanks to all that have answered. It was a hard decision to choose which is the best acceptable answer as both are acceptable but StackOverflow won't allow >1 answer. I have chosen this as the acceptable answer as @Jack highlighted to me about array_merge_recursive function that I didn't realize existed.

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.