'How to build grouped tree array from flat array in php

i am trying to re format a array as tree, main array and child arrays should be grouped by "name" property.

my flat array is like

$flat = [
    ['id' => 1, 'parent_id' => 0, 'name' => 'root1'],
    ['id' => 2, 'parent_id' => 0, 'name' => 'root1'],
    ['id' => 3, 'parent_id' => 1, 'name' => 'ch-1'],
    ['id' => 4, 'parent_id' => 1, 'name' => 'ch-1'],
    ['id' => 5, 'parent_id' => 3, 'name' => 'ch-1-1'],
    ['id' => 6, 'parent_id' => 3, 'name' => 'ch-1-1'],
    ['id' => 7, 'parent_id' => 0, 'name' => 'root2'],
    ['id' => 8, 'parent_id' => 0, 'name' => 'root2'],
    ['id' => 9, 'parent_id' => 7, 'name' => 'ch3-1'],
    ['id' => 10, 'parent_id' => 7, 'name' => 'ch3-1']
];

i could build tree structure by

$tree = buildTree($flat, 'parent_id', 'id');

function buildTree(array $flatList)
{
    $grouped = [];
    foreach ($flatList as $node) {
        $grouped[$node['parent_id']][] = $node;
    }

    $fnBuilder = function ($siblings) use (&$fnBuilder, $grouped) {
        foreach ($siblings as $k => $sibling) {
            $id = $sibling['id'];
            if (isset($grouped[$id])) {
                $sibling['children'] = $fnBuilder($grouped[$id]);
            }
            $siblings[$k] = $sibling;
        }
        return $siblings;
    };

    return $fnBuilder($grouped[0]);
}

this works perfectly.

But what i want is build nested array should be grouped by it's "name" property.

so the final output should be like

[
  "root1": [{
      "id": 1,
      "parent_id": 0,
      "name": "root1",
      "children": [
        "ch-1": [{
            "id": 3,
            "parent_id": 1,
            "name": "ch-1",
            "children": [
              "ch-1-1": [{
                  "id": 5,
                  "parent_id": 3,
                  "name": "ch-1-1"
                },
                {
                  "id": 6,
                  "parent_id": 3,
                  "name": "ch-1-1"
                }
              ]
            ]
          },
          {
            "id": 4,
            "parent_id": 1,
            "name": "ch-1"
          }
        ]
      ]
    },
    {
      "id": 2,
      "parent_id": 0,
      "name": "root1"
    }
  ],
  "root2": [{
      "id": 7,
      "parent_id": 0,
      "name": "root2",
      "children": [
        "ch3-1": [{
            "id": 9,
            "parent_id": 7,
            "name": "ch3-1"
          },
          {
            "id": 10,
            "parent_id": 7,
            "name": "ch3-2"
          }
        ]
      ]
    },
    {
      "id": 8,
      "parent_id": 0,
      "name": "root2"
    }
  ]
]

I have been stuck here almost for couple of days. Please help me to solve this problem. Thank you.



Solution 1:[1]

Use a function like these with unset()

function buildTree(array &$flat, $parentId = 0) {
    $branch = array();

    foreach ($flat as $element) {
        if ($element['parent_id'] == $parentId) {
            $children = buildTree($flat, $element['id']);
        if ($children) {
            $element['children'] = $children;
        }
        $branch[$element['id']] = $element;
        unset($flat[$element['id']]);
        }
    }
    return $branch;
}

After buildTree we need to group by name here is my code:-

$result = array();
foreach ($buildTree as $element) {
    $result[$element['name']][] = $element;
}

Hope it will help you. see details from here

Thanks

Solution 2:[2]

function buildTree2(array $flatList)
{
    $groupedchildren = [];
    $groupedparents = [];
    foreach ($flatList as $node) {
        if($node['parent_id'] == 0){
            $groupedparents[$node['parent_id']][] = $node;
        }else{
            $groupedchildren[$node['parent_id']][] = $node;
        }
    }
    
    $namegroupedparents = [];
    foreach ($groupedparents as $parent_group) {
        foreach ($parent_group as $node) {
            $namegroupedparents[$node['name']][] = $node;
        }
    }
    
    $namegroupedchildren = [];
    foreach ($groupedchildren as $children_group) {
        foreach ($children_group as $node) {
            $namegroupedchildren[$node['name']][] = $node;
        }
    }
    
    $fnBuilder = function (&$namegroupedparents) use (&$fnBuilder, $namegroupedchildren) {
        foreach($namegroupedparents as &$named){
            foreach($named as &$parentgroup){
                $id = $parentgroup['id'];
                foreach($namegroupedchildren as $thename => $namedall){
                    foreach($namedall as $childgroup){
                        if($childgroup['parent_id'] == $id){
                            if(isset($parentgroup['children'])){
                                if(!in_array($childgroup, $parentgroup['children'][$thename])){
                                    $parentgroup['children'][$thename][] = $childgroup;
                                }
                            }else{
                                $parentgroup['children'][$thename][] = $childgroup;
                            }
                            $fnBuilder($parentgroup['children']);
                        }
                    }
                }
            }
        } 
        return;
    };

    $fnBuilder($namegroupedparents);
    
    return $namegroupedparents;
}

This is working for your current array, but I don't know if it is going to work with different input.

It groups by parent_id then by name while putting the 0 parents in a different array than the rest of the children. Then it builds the children array by recursion.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Ibrahim Mohamed