1

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.

4
  • Is there any chance to have same name but different parent_id? Commented Feb 9, 2022 at 9:54
  • No.. There can not be have same name but different parent_id. Commented Feb 9, 2022 at 10:13
  • Does this answer your question? Build a tree from a flat array in PHP Commented Feb 9, 2022 at 10:13
  • I could build the tree array, but problem is formatted array should be grouped. Commented Feb 9, 2022 at 10:20

3 Answers 3

2
function buildTree(array $flat)
    {
        $grouped = [];
        foreach ($flat 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]);
    }


$tree = buildTree($flat);

pass tree structure array to groupedTree();

$groupd = groupedTree($tree);

echo json_encode($groupd, JSON_PRETTY_PRINT);


function groupedTree($tree)
    {
        $groupedByFuncTableName = array_reduce($tree, function (array $accumulator, array $element) {
            if (isset($element['children'])) {
                $element['children'] = groupedTree($element['children']);
            }
            $accumulator[$element['name']][] = $element;

            return $accumulator;
        }, []);
        return $groupedByFuncTableName;
    }

Sign up to request clarification or add additional context in comments.

Comments

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

5 Comments

Formatted array should be grouped by "name" property. But this doesn't group array correctly
Okay, @IsuruRodrigo! My bad, let me update this answer.
Hey, @IsuruRodrigo, could you please check this code from here onecompiler.com/php/3xsv4ucvt Also, I'm updated my answer.
your answer almost correct but this doesn't group nested objects. it only group parent objects.seems need to do this using recursive function. but i got no idea
Ok, I wish I help you to get the right result. 🙂
1
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.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.