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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.