简体   繁体   中英

PHP: Convert an array with a path attribute to a tree structure

Lets say, I have the following array:

$array = array(
    array(
        "id"   => 1,
        "name" => "Europe",
        "path" => "/"
    ),
    array(
        "id"   => 2,
        "name" => "Germany",
        "path" => "/1/"
    ),
    array(
        "id"   => 3,
        "name" => "France",
        "path" => "/1/"
    ),
    array(
        "id"   => 4,
        "name" => "Berlin",
        "path" => "/1/2/"
    ),
    array(
        "id"   => 5,
        "name" => "Munich",
        "path" => "/1/2/"
    )
);

As you can see, its a multidimensional array with 3 properites in earch 2nd level array: id, name and path. The path is a path structure based on the parent-id of its parent. For example, Germany (id=2) has belongs to Europe, so the path is "/1/" (ID 1 = Europe) and Berlin in Germany has the path "/1/2/" which means "/Europe/Germany/"

Now, I am trying to create a tree-array out of this, which should somehow look like:

$result = array(
    1 => array(
        "id" => 1,
        "name" => "Europe",
        "path" => "/",
        "childs" => array(
            2 => array(
                "id" => 2,
                "name" => "Germany",
                "path" => "/1/",
                "childs" => array(
                    4 => array(
                        "id"   => 4,
                        "name" => "Berlin",
                        "path" => "/1/2/"
                    ),
                    5 => array(
                        "id"   => 5,
                        "name" => "Munich",
                        "path" => "/1/2/"
                    )
                )
            ),
            3 => array(
                "id"   => 3,
                "name" => "France",
                "path" => "/1/"
            )
        )
    )
);

I have already tried to create a function with internal references, but this didn't works for me:

public static function pathToTree($items) {
    $array = array();
    $result = array();

    foreach($items AS $res) {
        $ids = explode("/", ltrim($res["path"] . $res["id"], "/"));
        $count = count($ids);
        $tmp = &$result;

        foreach( $ids AS $id) {
            if($count == 1) {
                $tmp = $res;
                $tmp["childs"] = array();
                $tmp = &$tmp["childs"];
            }
            else {
                $tmp[$id] = array(
                    "childs" => array()
                );
                $tmp = &$tmp[$id]["childs"];
            }
            $count--;
        }
    }

    return $array;
}

Ok, I think I just found a solution:

function pathToTree($array){
    $tree = array();
    foreach($array AS $item) {
        $pathIds = explode("/", ltrim($item["path"], "/") . $item["id"]);
        $current = &$tree;
        foreach($pathIds AS $id) {
            if(!isset($current["childs"][$id])) $current["childs"][$id] = array();
            $current = &$current["childs"][$id];
            if($id == $item["id"]) {
                $current = $item;
            }
        }
    }
    return $tree["childs"];
}

This is a dynamice solution for 1-n depth. Look at my example at http://ideone.com/gn0XLp . Here I tested it with some level:

  1. Continent
  2. Country
  3. City
  4. City-District
  5. City-Subdistrict
  6. City Sub-Sub-District

As I said in the comment, you need at least three loops to achieve your goals. Here's the function:

function pathToTree($array){
    $tree = Array();
    for($i=0; $i < count($array); $i++){
        if(substr_count($array[$i]["path"], '/') == 1)
            $tree[$array[$i]["id"]] = $array[$i];
    }
    for($i=0; $i < count($array); $i++){
        if(substr_count($array[$i]["path"], '/') == 2){
            $num = (int)str_replace("/","",$array[$i]["path"]);
            $tree[$num]["childs"][$array[$i]["id"]] = $array[$i];
        }
    }
    for($i=0; $i < count($array); $i++){
        if(substr_count($array[$i]["path"], '/') == 3){
            $num = explode("/", $array[$i]["path"]);
            $tree[$num[1]]["childs"][$num[2]]["childs"][$array[$i]["id"]] = $array[$i];
        }
    }
    return $tree;
}

Example:

Consider this array:

$array = array(
    array(
        "id"   => 1,
        "name" => "Europe",
        "path" => "/"
    ),
    array(
        "id"   => 2,
        "name" => "Germany",
        "path" => "/1/"
    ),
    array(
        "id"   => 3,
        "name" => "France",
        "path" => "/1/"
    ),
    array(
        "id"   => 4,
        "name" => "Berlin",
        "path" => "/1/2/"
    ),
    array(
        "id"   => 5,
        "name" => "Munich",
        "path" => "/1/2/"
    ),
    array(
        "id"   => 6,
        "name" => "Asia",
        "path" => "/"
    ),
    array(
        "id"   => 7,
        "name" => "China",
        "path" => "/6/"
    ),
    array(
        "id"   => 8,
        "name" => "Bangladesh",
        "path" => "/6/"
    ),
    array(
        "id"   => 9,
        "name" => "Beijing",
        "path" => "/6/7/"
    ),
    array(
        "id"   => 10,
        "name" => "Dhaka",
        "path" => "/6/8/"
    )
);

if I ran this code:

print_r(pathToTree($array));

the output will be:

Array
(
    [1] => Array
        (
            [id] => 1
            [name] => Europe
            [path] => /
            [childs] => Array
                (
                    [2] => Array
                        (
                            [id] => 2
                            [name] => Germany
                            [path] => /1/
                            [childs] => Array
                                (
                                    [4] => Array
                                        (
                                            [id] => 4
                                            [name] => Berlin
                                            [path] => /1/2/
                                        )

                                    [5] => Array
                                        (
                                            [id] => 5
                                            [name] => Munich
                                            [path] => /1/2/
                                        )

                                )

                        )

                    [3] => Array
                        (
                            [id] => 3
                            [name] => France
                            [path] => /1/
                        )

                )

        )

    [6] => Array
        (
            [id] => 6
            [name] => Asia
            [path] => /
            [childs] => Array
                (
                    [7] => Array
                        (
                            [id] => 7
                            [name] => China
                            [path] => /6/
                            [childs] => Array
                                (
                                    [9] => Array
                                        (
                                            [id] => 9
                                            [name] => Beijing
                                            [path] => /6/7/
                                        )

                                )

                        )

                    [8] => Array
                        (
                            [id] => 8
                            [name] => Bangladesh
                            [path] => /6/
                            [childs] => Array
                                (
                                    [10] => Array
                                        (
                                            [id] => 10
                                            [name] => Dhaka
                                            [path] => /6/8/
                                        )

                                )

                        )

                )

        )

)

Here's the phpfiddle link in case you might try it yourself.

I've created a recursive function:

// The array

$array = array(
    array(
        "id"   => 1,
        "name" => "Europe",
        "path" => "/"
    ),
    array(
        "id"   => 2,
        "name" => "Germany",
        "path" => "/1/"
    ),
    array(
        "id"   => 3,
        "name" => "France",
        "path" => "/1/"
    ),
    array(
        "id"   => 4,
        "name" => "Berlin",
        "path" => "/1/2/"
    ),
    array(
        "id"   => 5,
        "name" => "Munich",
        "path" => "/1/2/"
    ),
    array(
        "id"   => 6,
        "name" => "Asia",
        "path" => "/"
    ),
    array(
        "id"   => 7,
        "name" => "India",
        "path" => "/6/"
    ),
    array(
        "id"   => 7,
        "name" => "Mumbai",
        "path" => "/6/7"
    ),
    array(
        "id"   => 8,
        "name" => "Delhi",
        "path" => "/6/7"
    ),
);

// The recursive function

function createTree($input, &$result = array(), $key = null) {
    if ($key == "id") {
        $result["temp"]["id"] = $input;
    }
    if ($key == "name") {
        $result["temp"]["name"] = $input;
    }
    if ($key == "path") {
        $result["temp"]["path"] = $input;
        $levels = is_string($input) ? array_values(array_filter(explode('/', $input))) : null;
        if ($input == "/") {
            $result[$result["temp"]["id"]] = $result["temp"];
        }
        if (count($levels) == 1) {
            $result[$levels[0]]["childs"][$result["temp"]["id"]] = $result["temp"];
        }
        if (count($levels) == 2) {
            $result[$levels[0]]["childs"][$levels[1]]["childs"][$result["temp"]["id"]] = $result["temp"];
        }
        unset($result["temp"]);
    }
    if (is_array($input)) {
        foreach($input as $key => $value) {
            createTree($value, $result, $key);
        }
    }
    return $result;
}

// The result

array (
  1 => 
  array (
    'id' => 1,
    'name' => 'Europe',
    'path' => '/',
    'childs' => 
    array (
      2 => 
      array (
        'id' => 2,
        'name' => 'Germany',
        'path' => '/1/',
        'childs' => 
        array (
          4 => 
          array (
            'id' => 4,
            'name' => 'Berlin',
            'path' => '/1/2/',
          ),
          5 => 
          array (
            'id' => 5,
            'name' => 'Munich',
            'path' => '/1/2/',
          ),
        ),
      ),
      3 => 
      array (
        'id' => 3,
        'name' => 'France',
        'path' => '/1/',
      ),
    ),
  ),
  6 => 
  array (
    'id' => 6,
    'name' => 'Asia',
    'path' => '/',
    'childs' => 
    array (
      7 => 
      array (
        'id' => 7,
        'name' => 'India',
        'path' => '/6/',
        'childs' => 
        array (
          7 => 
          array (
            'id' => 7,
            'name' => 'Mumbai',
            'path' => '/6/7',
          ),
          8 => 
          array (
            'id' => 8,
            'name' => 'Delhi',
            'path' => '/6/7',
          ),
        ),
      ),
    ),
  ),
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM