MechaStorm
MechaStorm

Reputation: 1482

How do I transform this array into a multi-dimensional array via recursion?

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.

Upvotes: 1

Views: 169

Answers (2)

Ja͢ck
Ja͢ck

Reputation: 173522

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

Upvotes: 1

Joseph Silber
Joseph Silber

Reputation: 219920

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

Upvotes: 3

Related Questions