0
\$\begingroup\$

I have a simple array in PHP like this :

Array
(
    [max_size_video] => 50000
    [max_size_photo] => 8000
    [token_expire] => 100
    [dns] => mydns.fr
    ...
)

I would like to convert this array to multidimensional format with underscore as a separator:

Array
(
    [max] => Array
        (
            [size] => Array
                (
                    [video] => 50000
                    [photo] => 8000
                )
        )
    [token] => Array
        (
            [expire] => 100
        )
    [dns] => mydns.fr
    ...
)

I can do this with the following ugly code :

$item = explode('_', $row);
switch (count($item)) {
  case 1:
    $array[$item[0]] = $value;
  break;
  case 2:
    $array[$item[0]][$item[1]] = $value;
  break;
  case 3:
    $array[$item[0]][$item[1]][$item[2]] = $value;
  break;
  case 3:
    $array[$item[0]][$item[1]][$item[2]][$item[3]] = $value;
  break;
  ...
}

How can I do this with a more elegant function?

\$\endgroup\$
0

2 Answers 2

2
\$\begingroup\$

A reference variable will save you from that wicked code bloat!

You can make iterative calls of this Stack Overflow answer from 2012.

Code: (Demo)

function assignArrayByPath(&$arr, $path, $value, $separator = '_') {
    $keys = explode($separator, $path);
    foreach ($keys as $key) {
        $arr = &$arr[$key];
    }
    $arr = $value;
}
    
foreach ($array as $key => $value) {
    unset($array[$key]);
    assignArrayByPath($array, $key, $value);
}
var_export($array);

Or boil it down to this: (Demo)

function assignArrayByPath(&$result, $path, $value) {
    foreach (explode('_', $path) as $key) {
        $result = &$result[$key];
    }
    $result = $value;
}

$result = [];
foreach ($array as $key => $value) {
    assignArrayByPath($result, $key, $value);
}
var_export($result);

This technique will require that you build logical (non-monkey-wrenching) paths as keys. I mean:

  1. If after 'max_size_photo' => 8000, you declare 'max' => 10000, then you will get an error.
  2. If after 'max_size_photo' => 8000, you declare 'max_size' => 10000, then you will erase the previously expanded (deeper) elements from the output array.

p.s. You can also write it without any custom functions by nesting the loops so long as you maintain the technique of using a reference variable inside of a reference variable. Trippy, right?

Code: (Demo)

$result = [];
foreach ($array as $path => $value) {
    $ref = &$result;
    foreach (explode('_', $path) as $key) {
        $ref = &$ref[$key];
    }
    $ref = $value;
}
var_export($result);
\$\endgroup\$
4
  • \$\begingroup\$ Thank you very much. It’s for get data in a database, and send in json at my frontend \$\endgroup\$
    – Lokomass
    Commented Jan 27, 2021 at 6:47
  • \$\begingroup\$ If you have any power to change the database values, it would be better to convert this data to JSON before saving it to the database. Then you only need to call the standard json_decode() when you pull the row. Modern versions of MySQL offer JSON manipulations too (but you might not need them for your usage). \$\endgroup\$ Commented Jan 27, 2021 at 6:59
  • \$\begingroup\$ @mickmackusa what's the logic behind having assignArrayByPath as a function as opposed to a simple nested loop? \$\endgroup\$
    – Steven
    Commented Jan 30, 2021 at 13:12
  • 1
    \$\begingroup\$ @Steven I added the nested loop version. \$\endgroup\$ Commented Jan 30, 2021 at 20:53
0
\$\begingroup\$

Mick already provided a good solution per hour question.

Bearing in mind it might have just been for illustration purposes, I noticed the switch statement has a redundant case:


 case 3:
    $array[$item[0]][$item[1]][$item[2]] = $value;
  break;
  case 3:
    $array[$item[0]][$item[1]][$item[2]][$item[3]] = $value;
  break;

Here the second case 3 is unreachable- perhaps it was a typo and should have been case 4. This won’t lead to an error but is excess lines of code that could lead to confusion for anyone reading the code- including your future self!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.