Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert Flat PHP Array to Nested Array based on Array Keys?

I need to convert a flat array where the array keys indicate the structure into a nested array where the parent element becomes element zero, i.e. in the example:

$education['x[1]'] = 'Georgia Tech';

It needs to be converted to:

$education[1][0] = 'Georgia Tech';

Here is an example input array:

$education = array(
  'x[1]'     => 'Georgia Tech',
  'x[1][1]'  => 'Mechanical Engineering',
  'x[1][2]'  => 'Computer Science',
  'x[2]'     => 'Agnes Scott',
  'x[2][1]'  => 'Religious History',
  'x[2][2]'  => 'Women\'s Studies',
  'x[3]'     => 'Georgia State',
  'x[3][1]'  => 'Business Administration',
);

And here is what the output should be:

$education => array(
  1 => array(
    0 => 'Georgia Tech',
    1 => array( 0 => 'Mechanical Engineering' ),
    2 => array( 0 => 'Computer Science' ),
  ),
  2 => array(
    0 => 'Agnes Scott',
    1 => array( 0 => 'Religious History' ),
    2 => array( 0 => 'Women\'s Studies' ),
  ),
  3 => array(
    0 => 'Georgia State',
    1 => array( 0 => 'Business Administration' ),
  ),
);

I've banged my head against the wall for hours and still can't get it working. I think I've been looking at it too long. Thanks in advance.

P.S. It should be fully nestable, i.e. it should be able to convert a key that looks like this:

x[1][2][3][4][5][6] 

P.P.S. @Joseph Silber had a clever solution but unfortunately using eval() is not an option for this as it's a WordPress plugin and the WordPress community is trying to stamp out the use of eval().

like image 754
MikeSchinkel Avatar asked Aug 19 '11 03:08

MikeSchinkel


2 Answers

Based on the first suggestion above I found a solution that worked for my .ini file by modifying the $ancestors variable.

EDIT: Here is a complete version of my working code: https://stackoverflow.com/a/38480646/1215633

//$ancestors = explode('][', substr($path, 2, -1));
$ancestors = explode('.', $path);

I had this setup in my array, based on an .ini file:

[resources.db.adapter] => PDO_MYSQL
[resources.db.params.host] => localhost
[resources.db.params.dbname] => qwer
[resources.db.params.username] => asdf
[resources.db.params.password] => zxcv
[resources.db.params.charset] => utf8
[externaldb.adapter] => PDO_MYSQL
[externaldb.params.host] => localhost
[externaldb.params.dbname] => tyui
[externaldb.params.username] => ghjk
[externaldb.params.password] => vbnm
[externaldb.params.charset] => latin1

The outcome became as desired:

Array
(
[resources] => Array
    (
        [db] => Array
            (
                [adapter] => PDO_MYSQL
                [params] => Array
                    (
                        [host] => localhost
                        [dbname] => qwer
                        [username] => asdf
                        [password] => zxcv
                        [charset] => utf8
                    )

            )

    )

[externaldb] => Array
    (
        [adapter] => PDO_MYSQL
        [params] => Array
            (
                [host] => localhost
                [dbname] => tyui
                [username] => ghjk
                [password] => vbnm
                [charset] => latin1
            )

    )

)
like image 95
orjtor Avatar answered Oct 20 '22 03:10

orjtor


Here is some code to handle what you had originally proposed as output.

/**
 * Give it and array, and an array of parents, it will decent into the
 * nested arrays and set the value.
 */
function set_nested_value(array &$arr, array $ancestors, $value) {
  $current = &$arr;
  foreach ($ancestors as $key) {

    // To handle the original input, if an item is not an array, 
    // replace it with an array with the value as the first item.
    if (!is_array($current)) {
      $current = array( $current);
    }

    if (!array_key_exists($key, $current)) {
      $current[$key] = array();
    }
    $current = &$current[$key];
  }

  $current = $value;
}


$education = array(
  'x[1]'     => 'Georgia Tech',
  'x[1][1]'  => 'Mechanical Engineering',
  'x[1][2]'  => 'Computer Science',
  'x[2]'     => 'Agnes Scott',
  'x[2][1]'  => 'Religious History',
  'x[2][2]'  => 'Women\'s Studies',
  'x[3]'     => 'Georgia State',
  'x[3][1]'  => 'Business Administration',
);

$neweducation = array();

foreach ($education as $path => $value) {
  $ancestors = explode('][', substr($path, 2, -1));
  set_nested_value($neweducation, $ancestors, $value);
}

Basically, split your array keys into a nice array of ancestor keys, then use a nice function to decent into the $neweducation array using those parents, and set the value.

If you want the output that you have updated your post to have, add this in the foreach loop after the line with 'explode'.

$ancestors[] = 0;
like image 33
loganfsmyth Avatar answered Oct 20 '22 05:10

loganfsmyth