Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unflatten array in PHP using dot notation

I have php array structure like this:

array(
    'servicemanagement.scheduler.events.edit' => 'Edit',
    'servicemanagement.scheduler.events.delete' => 'Delete',
    'servicemanagement.scheduler.events' => 'Events',
    'servicemanagement.scheduler' => 'Scheduler',
    'servicemanagement.subscribers' => 'Subscribers',
    'servicemanagement.subscribers.index' => 'Index',
    'servicemanagement' => 'Service management',
);

And I would like to convert is to multidimensional array like:

array(
    'servicemanagement' => array(
        'id' => 'servicemanagement',
        'title' => 'Service Management',
        'children' => array(
            'scheduler' => array(
                'id' => 'servicemanagement.scheduler',
                'title' => 'Scheduler',
                'children' => array(
                    'events' => array(
                        'id' => 'servicemanagement.scheduler.events',
                        'title' => 'Events',
                        'children' => array(
                            'edit' => array(
                                'id' => 'servicemanagement.scheduler.events.edit',
                                'title' => 'Edit',
                                'children' => array(),
                            ),
                            'delete' => array(
                                'id' => 'servicemanagement.scheduler.events.delete',
                                'title' => 'Delete',
                                'children' => array(),
                            ),
                        ),
                    ),
                ),
            ),
            'subscribers' => array(
                'id' => 'servicemanagement.subscribers',
                'title' => 'Subscribers',
                'children' => array(
                    'index' => array(
                        'id' => 'servicemanagement.subscribers.index',
                        'title' => 'Index',
                    )
                ),
            ),
        ),
    ),
);

I have checked some answers already like this one: How to set a deep array in PHP

But it seems that i could not manage to clear up the writing on top of the arrays and the last record 'servicemanagement' removes all of the previous records.

The function that is used there is

function setArray(&$array, $keys, $value) {
    $keys = explode(".", $keys);
    $current = &$array;
    foreach($keys as $key) {
        $current = &$current[$key];
    }
    $current = $value;
}

Another function that I have found but it is not doing the expected result is:

function unflatten($array,$prefix = '')
{
    $result = array();
    foreach($array as $key=>$value)    {
        if (!empty($prefix)) {
            $key = preg_replace('#^'.preg_quote($prefix).'#','',$key);
        }
        if (strpos($key,'.') !== false) {
            parse_str('result['.str_replace('.','][',$key)."]=".$value);
        } else {
            $result[$key] = $value;
        }
    }
    return $result;
}

It is an option to use recursion to unflatten this array since the end format is the same for all records.

May anyone give me a tip ot this one?

like image 716
kachar Avatar asked Oct 24 '25 10:10

kachar


2 Answers

I created an unflatten function for reference here:

https://gist.github.com/Gerst20051/b14c05b72c73b49bc2d306e7c8b86223

$results = [
  'id' => 'abc123',
  'address.id' => 'def456',
  'address.coordinates.lat' => '12.345',
  'address.coordinates.lng' => '67.89',
  'address.coordinates.geo.accurate' => true,
];

function unflatten($data) {
  $output = [];
  foreach ($data as $key => $value) {
    $parts = explode('.', $key);
    $nested = &$output;
    while (count($parts) > 1) {
      $nested = &$nested[array_shift($parts)];
      if (!is_array($nested)) $nested = [];
    }
    $nested[array_shift($parts)] = $value;
  }
  return $output;
}

echo json_encode(unflatten($results));

/*
{
  "id": "abc123",
  "address": {
    "id": "def456",
    "coordinates": {
      "lat": "12.345",
      "lng": "67.89",
      "geo": {
        "accurate": true
      }
    }
  }
}
*/

This was slightly influenced by the following resources:

  • https://gist.github.com/tanftw/8f159fec2c898af0163f
  • https://medium.com/@assertchris/dot-notation-3fd3e42edc61
like image 137
Andrew Avatar answered Oct 25 '25 22:10

Andrew


This isn't the cleanest solution but it works as a single function

    $your_array = array(
        'servicemanagement.scheduler.events.edit' => 'Edit',
        'servicemanagement.scheduler.events.delete' => 'Delete',
        'servicemanagement.scheduler.events' => 'Events',
        'servicemanagement.scheduler' => 'Scheduler',
        'servicemanagement.subscribers' => 'Subscribers',
        'servicemanagement.subscribers.index' => 'Index',
        'servicemanagement' => 'Service management',
    );

    function expand($array, $level = 0)
    {
        $result = array();
        $next = $level + 1;

        foreach($array as $key=>$value) {
            $tree = explode('.', $key);
            if(isset($tree[$level])) {
                if(!isset($tree[$next])) {
                    $result[$tree[$level]]['id'] =  $key;
                    $result[$tree[$level]]['title'] = $value;
                    if(!isset($result[$tree[$level]]['children'])) {
                        $result[$tree[$level]]['children'] = array();
                    }
                } else {
                    if(isset($result[$tree[$level]]['children'])) {
                        $result[$tree[$level]]['children'] = array_merge_recursive($result[$tree[$level]]['children'], expand(array($key => $value), $next));
                    } else {
                        $result[$tree[$level]]['children'] = expand(array($key => $value), $next);
                    }
                }

            }
        }

        return $result;

    }
   var_export(expand($your_array));
like image 40
Mike Avatar answered Oct 25 '25 22:10

Mike



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!