Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Group flat array to multidimensional array by key, If we don't know how many levels are there. PHP

Tags:

I have this array:

$arr = [
["id"=>20,
    "name"=>"a",
    "parent"=>28,
],
["id"=>21,
    "name"=>"a-child",
    "parent"=>20,
],
["id"=>27,
    "name"=>"a-child-b",
    "parent"=>20,
],
["id"=>28,
   "name"=>"A parent",
   "parent"=>0,
],
["id"=>12,
    "name"=>"no parent",
    "parent"=>0,
]];

What I want is to group it based on parent key, where parent = id && parent > 0 or the id is the parent for this element and the element has parent, if the parent key is greater than zero.

In the above array id=12 has no parent, id=20 has child 21, 27 and it is a child for id=28.

What I did :

    public function sort($arr){

    $result = [];

    // Get child
    foreach($arr as $key => $row) {
        if($row['parent'] > 0) {
            $result[$row->parent][] = ['id' => $row['id'], 'name' => $row['name']];
            unset($arr[$key]);
        }
    }

    // Get parent and append child
    foreach($arr as $key => $row) {
        $result[$row['id']] = ['name' => $row['name'],
                              'child' => $result[$row['id']]];

    }
    return $result;
}

That problem is that, this is only for 1 level of child like parent => child array().

What I want to make is a method which gets an argument (above array), where I don't know, how many levels of nesting I will have and returns grouped by parent key array:

$arr = [
["id"=>28,
 "name"=>"A parent",
 "parent"=>0,
    'child' => [
        ["id"=>20,
             "name"=>"a",
             "parent"=>28,
             'child' => [
                 ["id"=>21,
                  "name"=>"a-child",
                  "parent"=>20,
                 ],
                 ["id"=>27,
                  "name"=>"a-child-b",
                  "parent"=>20,
                 ]
             ]
        ]
    ]
],
["id"=>12,
 "name"=>"no parent",
 "parent"=>0,
]];
like image 302
gdfgdfg Avatar asked Dec 17 '18 16:12

gdfgdfg


People also ask

What is multidimensional array in PHP explain it with simple PHP code?

A multidimensional array is an array containing one or more arrays. PHP supports multidimensional arrays that are two, three, four, five, or more levels deep. However, arrays more than three levels deep are hard to manage for most people.

How do you flat a multidimensional array?

flat() is a new array instance method that can create a one-dimensional array from a multidimensional array. By default it only “flats” up to one level. You can add a parameter to flat() to set the number of levels you want to flat the array to.

How to get keys of an array in PHP?

PHP: array_keys() function The array_keys() function is used to get all the keys or a subset of the keys of an array. Note: If the optional search_key_value is specified, then only the keys for that value are returned. Otherwise, all the keys from the array are returned.

How to define multiple array in PHP?

PHP allows a very simple way to declare a multidimensional array in PHP using the keyword 'array'. In order to declare an array inside another array, We need to add the keyword 'array' and then the elements of that array.


2 Answers

<?php

define('ROOT_PARENT',0);

function getHierarchy($records){
    $hierarchy = [];

    /*
        let's assume everybody is going to be a parent
    */

    foreach($records as $each_record){
        $each_record['child'] = [];
        $hierarchy[$each_record['id']] = $each_record;
    }

    /*
       Now add child to parent's key in $hierarchy in the 'child' key. 
       The & is important since there may be future childs for current child. So pass by reference is needed
    */
    foreach($records as $each_record){
        $hierarchy[$each_record['parent']]['child'][] = &$hierarchy[$each_record['id']];
    }

    /* 
        here I unset every key which wasn't at root level,i.e is 0(top) level
    */
    foreach($hierarchy as $parent => $its_data){
        if($parent != ROOT_PARENT){
            unset($hierarchy[$parent]);
        }
    }

    return isset($hierarchy[ROOT_PARENT],$hierarchy[ROOT_PARENT]['child']) ? $hierarchy[ROOT_PARENT]['child'] : [];
}

$records = [
            [
                "id" => 20,
                "name" => "a",
                "parent" => 28,
            ],
            [
                "id" => 21,
                "name" => "a-child",
                "parent" => 20,
            ],
            [
                "id" => 27,
                "name" => "a-child-b",
                "parent" => 20,
            ],
            [
                "id" => 28,
               "name" => "A parent",
               "parent" => 0,
            ],
            [
                "id" => 12,
                "name" => "no parent",
                "parent" => 0,
            ]
    ];


echo "<pre>";
print_r(getHierarchy($records));

Output:

Array
(
    [0] => Array
        (
            [id] => 28
            [name] => A parent
            [parent] => 0
            [child] => Array
                (
                    [0] => Array
                        (
                            [id] => 20
                            [name] => a
                            [parent] => 28
                            [child] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 21
                                            [name] => a-child
                                            [parent] => 20
                                            [child] => Array
                                                (
                                                )

                                        )

                                    [1] => Array
                                        (
                                            [id] => 27
                                            [name] => a-child-b
                                            [parent] => 20
                                            [child] => Array
                                                (
                                                )

                                        )

                                )

                        )

                )

        )

    [1] => Array
        (
            [id] => 12
            [name] => no parent
            [parent] => 0
            [child] => Array
                (
                )

        )

)

At first, we consider everybody could be a parent. Then, in it's parent's child key, we keep adding it's children. We pass the key by reference since there may be future children. Finally, unset() everybody from the hierarchy that is not a root parent. In the end, you have your final hierarchy structure.

like image 75
nice_dev Avatar answered Sep 20 '22 08:09

nice_dev


Here's a two-step approach that first builds an associative array mapping parents to arrays of their children, then recursively populates children keys by indexing into the parent-child associative array.

Note that I assume parent => 0 to be the root but this is also adjustable. I also used the key children, which I feel is more semantic, but feel free to revert to child.

function insert(&$curr, $parents) {
    if (array_key_exists($curr['id'], $parents)) {
        $curr['children'] = $parents[$curr['id']];
    }

    if (array_key_exists('children', $curr)) {
        foreach ($curr['children'] as &$child) {
            insert($child, $parents);
        }
    }
}

function treeify($arr) {
    foreach ($arr as $e) {
        $parents[$e['parent']][] = $e;
    }

    foreach ($parents[0] as &$root) {
        insert($root, $parents);
    }

    return $parents[0];
}

Output:

Array
(
    [0] => Array
        (
            [id] => 28
            [name] => A parent
            [parent] => 0
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 20
                            [name] => a
                            [parent] => 28
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 21
                                            [name] => a-child
                                            [parent] => 20
                                        )

                                    [1] => Array
                                        (
                                            [id] => 27
                                            [name] => a-child-b
                                            [parent] => 20
                                        )

                                )

                        )

                )

        )

    [1] => Array
        (
            [id] => 12
            [name] => no parent
            [parent] => 0
        )

)

Try it!

like image 30
ggorlen Avatar answered Sep 21 '22 08:09

ggorlen