tags:

views:

199

answers:

2

Whats the easiest way to invert a multidimensional array. By invert I mean similar to array_flip.

e.g

[0][5][var_name] = data
[0][3][var_name2] = data2
[1][var_name3] = data3
[0][1][4][var_name4] = data4

inverted would be:

[var_name][0][5] = data
[var_name2][0][3] = data2
[var_name3][1] = data3
[var_name4][0][1][4] = data4

Any ideas?

+1  A: 

I couldn't think of an easy way to do this. So I wrote up a really complicated way to do it. Namely:

  1. Take the multidimensional array and flatten it into a list of keys and values.
  2. Reverse the keys.
  3. Unflatten the list to obtain an inverted multidimensional array.

Code

<?php
function print_entries($array, $prekeys = array())
{
    foreach ($array as $key => $value)
    {
        $keys = array_merge($prekeys, array($key));

        if (is_array($value))
            print_entries($value, $keys);
        else
            echo '[' . implode('][', $keys) . "] = $value\n";
    }
}

function flatten_array($array)
{
    $entries = array();

    foreach ($array as $key => $value)
    {
        if (is_array($value))
        {
            foreach (flatten_array($value) as $subentry)
            {
                $subkeys  = $subentry[0];
                $subvalue = $subentry[1];

                $entries[] = array(array_merge(array($key), $subkeys), $subvalue);
            }
        }
        else
            $entries[] = array(array($key), $value);
    }

    return $entries;
}

function unflatten_array($entries)
{
    $array = array();

    foreach ($entries as $entry)
    {
        $keys  = $entry[0];
        $value = $entry[1];

        $subarray = &$array;

        foreach ($keys as $i => $key)
        {
            if ($i < count($keys) - 1)
            {
                if (!isset($subarray[$key]))
                    $subarray[$key] = array();

                $subarray = &$subarray[$key];
            }
            else
                $subarray[$key] = $value;
        }
    }

    return $array;
}

function invert_array($array)
{
    $entries = flatten_array($array);

    foreach ($entries as &$entry)
        $entry[0] = array_reverse($entry[0]);

    return unflatten_array($entries);
}

$array = array
(
    0 => array
    (
        5 => array('var_name' => 'data'),
        3 => array('var_name2' => 'data2'),
        1 => array(4 => array('var_name4' => 'data4'))
    ),

    1 => array(0 => array('var_name' => 'data3'))
);

print_entries($array);
echo "\n";
print_entries(invert_array($array));
?>

Output

[0][5][var_name] = data
[0][3][var_name2] = data2
[0][1][4][var_name4] = data4
[1][0][var_name] = data3

[var_name][5][0] = data
[var_name2][3][0] = data2
[var_name4][4][1][0] = data4
[var_name][0][1] = data3

Edit: I noticed now that you don't reverse the keys but you simply move the var_name portion from the end to the front and leave the numerical indices alone. It's easy enough to modify the line in flatten_array where I call array_reverse to re-order the keys in a different way. The core flatten/unflatten logic would not need to be changed. I leave this as an exercise for the reader. :-)

John Kugelman
+1  A: 

You could store a list of the keys of all ancestors and when you hit a "leaf" use this list to create the "flipped" version.

<?php
$data = array(
  0=>array(
    5=>array('var_name' => 'data'),
    3=>array('var_name2' => 'data2'),
    1=>array(4=>array('var_name4'=>'data4'))
  ),
  1=>array('var_name3'=>'data3')
);

$result = array();
foo($data, $result);

($result['var_name'][0][5] === 'data') or die('1');
($result['var_name2'][0][3] === 'data2') or die('2');
($result['var_name3'][1] === 'data3') or die('3');
($result['var_name4'][0][1][4] === 'data4') or die('4');
echo 'ok';


function foo(array $a, array &$target, $stack=array()) {
  foreach($a as $key=>$value) {
    if ( is_array($value) ) {
      array_push($stack, $key);
      foo($value, $target, $stack);
      array_pop($stack);
    }
    else {
      $target[$key] = array();
      $tmp = &$target[$key];
      foreach( $stack as $s ) { // now it's not so stack-ish anymore :-S
        $tmp[$s] = array();
        $tmp = &$tmp[$s];
      }
      $tmp = $value;
    }
  }
}
VolkerK