Search code examples
phparrayslaravelmultidimensional-arraydot-notation

Convert a multidimensional collection (array) to a flattened structure with dot-delimited keys in Laravel


Example input as json

{
   "user":{
      "name":"Thomas",
      "age":101
   },
   "shoppingcart":{
      "products":{
         "p1":"someprod",
         "p2":"someprod2"
      },
      "valuta":"eur",
      "coupon":null,
      "something":[
         "bla1",
         "bla2"
      ]
   }
}

Expected output

[
    'user.name' => 'Thomas',
    'user.age' => 101,
    'shoppingcart.products.p1' => 'someprod',
    ...
    'shoppingcart.something.1' => 'bla1'
]

I have written this function however it produces the wrong output. Next to that, I would like to rewrite said function to a macro for Collection but I cannot wrap my head around it. The problem is also that the current function as is requires a global var to keep track of the result.

public function dotFlattenArray($array, $currentKeyArray = []) {
    foreach ($array as $key => $value) {
        $explodedKey = preg_split('/[^a-zA-Z]/', $key);
        $currentKeyArray[] = end($explodedKey);
        if (is_array($value)) {
            $this->dotFlattenArray($value, $currentKeyArray);
        } else {
            $resultArray[implode('.', $currentKeyArray)] = $value;
            array_pop($currentKeyArray);
        }
    }
    $this->resultArray += $resultArray;
}

So my problem is twofold:

  1. Sometimes the function does not give the right result

  2. How can I rewrite this recursive function to a macro

    Collection::macro('dotflatten', function () { return .... });


Solution

  • The thing you are trying to do is called transform an multidimensional array to array with dot notation.

    You don't need to reinvent the wheel, Laravel have already a helper for it, called array_dot().

    The array_dot function flattens a multi-dimensional array into a single level array that uses "dot" notation to indicate depth:

    $array = ['products' => ['desk' => ['price' => 100]]];

    $flattened = array_dot($array);

    // ['products.desk.price' => 100]

    You only need to transform your json to array with json_decode() and then flat it with array_dot() to get an array with dot notation.