1

My question is how correctly convert from one element of array ['a_b_c' => 123] to ['a' => ['b' => ['c' => 123]]]; with any level!

As example input data(in tests), i use ['a_a' => '2', 'a_b' => '333', 'c_c' => '123'] as key separator i choose '_'

And waiting result like:
['a' => ['a' => '2', 'b' => '333'], 'c' => ['c' => '123']

but actual result is: ['a' => ['b' => '333'], 'c' => ['c' => '123']]

i use static function from class Helper:

 /**
     * @param array|string $data Inserted data, like regular array, or simple string(will return as is)
     * @param string $separator string separator to know what we had.
     * @return array|string
     */
    public static function unFlatArr($data, $separator = '_')
    {
        $return = [];

        if (is_iterable($data)) {
            foreach ($data as $oldKey => $value) {
                $keysArr = explode($separator, $oldKey, 2);
                $key = $keysArr[0];
                $k = $keysArr[1] ?? null;

                if (null !== $k) {
                    //got nesting.
                    $value = self::unFlatAr([$k => $value], $separator);
                }
                $return[$key] = $value;
            }

        } else {
            $return = $data;
        }

        return $return;
    }

Where i'm fail and why, please, tell me. thank you.

mickmackusa
  • 37,596
  • 11
  • 75
  • 105
Vladimir Ch
  • 498
  • 12
  • 24
  • 1
    I gave it [a try](https://3v4l.org/oYJH5#v8.1.5). All that is left is to join and flatten, might be of use? – Jaquarh May 08 '22 at 20:49
  • [Convert dot syntax like "this.that.other" to multi-dimensional array in PHP](https://stackoverflow.com/a/9636021/2943403) is called like this: https://3v4l.org/lVZjF or to pass in the whole array, like this: https://3v4l.org/N1vmF – mickmackusa May 15 '22 at 22:40
  • Thank, buy "like this: 3v4l.org/N1vmF" executed ~2x slower )))) `php V8.1 0.017(mine was 0.006)` `php V8.0 0.018(mine was 0.009)` `php V 7.4 0.014(mine was 0.013)` – Vladimir Ch May 18 '22 at 09:45
  • If you have a process that does the same thing, but is more performant than the advice on the duplicate, then the best thing to do would be to transfer your knowledge to the older duplicate because [the fundamental goal of closing duplicate questions is to help people find the right answer by getting all of those answers in one place.](https://stackoverflow.com/help/duplicates#:~:text=The%20fundamental%20goal%20of%20closing%20duplicate%20questions%20is%20to%20help%20people%20find%20the%20right%20answer%20by%20getting%20all%20of%20those%20answers%20in%20one%20place.) – mickmackusa May 18 '22 at 10:59
  • I am not blind, nor was my vote to close this page an attack on you. I'll ask you not to lash out at me while I am trying to consolidate content to try to make Stack Overflow a better resource for researchers. – mickmackusa May 18 '22 at 11:03
  • The accepted answer on that page must be called inside of a loop for multiple elements. https://3v4l.org/DvAY0/perf – mickmackusa May 18 '22 at 11:15
  • @mickmackusa updated answer, now works faster, but only with two levels. also as my solution, it give incorrect data on `$array = ['a_a' => '2', 'a_b' => '333', 'a_b_bb_c' => '333', 'c_c' => '123'];` input ! And about blind, i wrote cause there same but different solution in `Convert dot syntax like "this.that.other" to multi-dimensional array in PHP` but not same. as i tested on 3+ lvls of array! – Vladimir Ch May 19 '22 at 13:15
  • As I have just mentioned under your answer, your sample input has conflicting demands. Should `$array['a']['b']` be `333` or should it be `['bb' => ['c' => 333]]`? If the expected value for `$array['a']['b']` is `[0 => 333, 'bb' => ['c' => 333]]`, then the input expression for the first `333` value should be `a_b_0`. – mickmackusa May 23 '22 at 22:59

1 Answers1

2

Solution is to check content of joining, not only is_array() checking.

Converted to simple function(without class/static and etc):

<?php
// worked with any lvl
function unFlatArr($array, $delimiter = '_')
    {
    $result = [];
    foreach ($array as $notations => $value) {
        // extract keys
        $keys = explode($delimiter, $notations);
        // reverse keys for assignments
        $keys = array_reverse($keys);

        // set initial value
        $lastVal = $value;
        foreach ($keys as $key) {
            // wrap value with key over each iteration
            $lastVal = [
                $key => $lastVal
            ];
        }
        
        // merge result
        $result = array_merge_recursive($result, $lastVal);
    }

    return $result;
    }

$array = ['a_a' => '2', 'a_b' => '33', 'a_b_c' => '444', 'a_b_d' => '555', 'c_c' => '12'];
var_export(unFlatArr($array));

Worked correctly, thanx Ilya, Erkin!

And another solution by mickmackusa

did not work correctly with 2+ level of input array !

Vladimir Ch
  • 498
  • 12
  • 24
  • 1st solution works incorrect under 3+ lvls of explodable keys! `$array = ['a_a' => '2', 'a_b' => '333', 'a_b_bb_c' => '333', 'c_c' => '123'];` and 2nd solution fails, so be careful! – Vladimir Ch May 19 '22 at 13:18
  • The place to call out unsuitable techniques is on the earlier posted dupe target. If you find input where those answers fail, then post an answer on that old page that does not fail with your new sample data. [The fundamental goal of closing duplicate questions is to help people find the right answer by getting all of those answers in one place.](https://stackoverflow.com/help/duplicates#:~:text=The%20fundamental%20goal%20of%20closing%20duplicate%20questions%20is%20to%20help%20people%20find%20the%20right%20answer%20by%20getting%20all%20of%20those%20answers%20in%20one%20place.) – mickmackusa May 19 '22 at 20:05
  • link already posted May 18 at 10:59 ok, i'll post my answer on older topic too – Vladimir Ch May 23 '22 at 13:56
  • Regarding your first comment under this answer, it could be argued that your sample input is imprecise/conflicting about the expected output. `a_b => '333'` declares that `$array['a']['b']` should contain a scalar value of `333`. However, `a_b_bb_c => '333'` declares that `$array['a']['b']` should contain an array (non-scalar value) of `['bb' => ['c' => 333]]`. – mickmackusa May 23 '22 at 22:55