Home > Net >  How to convert a flat array into a tree array based on dot notation and "type" attribute?
How to convert a flat array into a tree array based on dot notation and "type" attribute?

Time:10-24

I want to convert a flat array below:

$array = [
   [
    "hierarchy" => "1",
    "title" => "Fruits",
    "type" => "category_label"
   ],
   [
    "hierarchy" => "1.1",
    "title" => "Citruses",
    "type" => "category_label"
   ],
   [
    "hierarchy" => "1.1.1",
    "title" => "Orange",
    "type" => "item"
   ],
   [
    "hierarchy" => "1.1",
    "title" => "Mango",
    "type" => "item"
   ],
   [
    "hierarchy" => "1.2",
    "title" => "Grape",
    "type" => "item"
   ]
];

As you can see from above, it has two hierarchical, dot-notation string "1.1", therefore I use the "type" attribute to differentiate them.

What the result I'm expecting:

/*
1. Fruits
-  1.1. Citruses
--- 1.1.1. Orange
-- 1.1. Mango
-- 1.2. Grape
*/
//Desired result
[
    "hierarchy" => "1",
    "title" => "Fruits",
    "type" => "category_label",
    "children" => [
        [
            "hierarchy" => "1.1",
            "title" => "Citruses",
            "type" => "category_label"
            "children" => [
                [
                    "hierarchy" => "1.1.1",
                    "title" => "Orange",
                    "type" => "item"
                ]
            ]
        ],
        [
            "hierarchy" => "1.1",
            "title" => "Mango",
            "type" => "item"
        ],
        [
            "hierarchy" => "1.2",
            "title" => "Grape",
            "type" => "item"
        ]
    ]
];

Using approach as described in How to build a tree from a concatenated string in PHP? I can't achieve my desired result. What I can do now?

CodePudding user response:

You could first key all items by their hierarchy in a new associative array. Then iterate the items again, and remove the last part of the hierarchy to get the key of the parent item, locate it in that new associative array, and add it to its children. If it does not have a parent, add it to the result array.

Since your hierarchy delimiter is a point, you can use the pathinfo function to remove the last "extension" from it:

It turns out your hierarchy numbering is not unique (e.g. "1.1"). If it is intended that parent nodes can have the same number as a sibling of type "item", but that two nodes of the same type will never have the same hierarchy, then key the nodes by the combination of type and hierarchy:

foreach ($array as $item) $keyed[$item["type"] . $item["hierarchy"]] = $item;

foreach ($keyed as &$item) {
    $parent = pathinfo($item["hierarchy"], PATHINFO_FILENAME);
    if ($parent == $item["hierarchy"]) $result[] =& $item;
    else $keyed["category_label$parent"]["children"][] =& $item;
}

After running this, $result will have the desired structure.

I would however suggest to make the hierarchy codes unique, so they don't clash across types. Then the above code can be changed to:

foreach ($array as $item) $keyed[$item["hierarchy"]] = $item;

foreach ($keyed as &$item) {
    $parent = pathinfo($item["hierarchy"], PATHINFO_FILENAME);
    if ($parent == $item["hierarchy"]) $result[] =& $item;
    else $keyed[$parent]["children"][] =& $item;
}

CodePudding user response:

See if this logic helps, I will try to edit the answer and explain within 1 day.

$tree_array = [];
foreach($array as $key => $value){
    if(count(explode('.',$value['hierarchy']) == 1){
        array_push($array_tree, $value)
    }
}

foreach($array as $value){
    if(count(explode('.',$value['hierarchy'])) == 2){
        if($value['type'] == 'category_label'){
            foreach($array_tree as $key =>  $tree){
                if($tree['hierarchy'] == explode('.',$value['hierarchy'])[0]){
                    array_push($array_tree['children'],$value);
                }
            }
        } else {
            foreach($array_tree as $key =>  $tree){
                if($tree['hierarchy'] == explode('.',$value['hierarchy'])[0]){
                    array_push($array_tree[$key],$value);
                }
            }
        }
    }
}

foreach($array as $value){
    if(count(explode('.',$value['hierarchy'])) == 3){
        if($value['type'] == 'item'){
            foreach($array_tree as $key => $tree){
                foreach($tree as $item){
                    if($item['hierarchy'] == explode('.',$value['hierarchy'],2)[1]){
                        array_push($array_tree[$key]['children'],$value);
                    }
                }
            }
        }
    }
}
  • Related