首页 > 解决方案 > 在 PHP 中创建具有给定深度、根和父级数量的子级的树结构

问题描述

我有一个用户,我将考虑我的树结构的根元素。之后,我有一组来自我的数据库的用户,我考虑了树的所有子元素。

树的构建需要包含以下决定树的宽度和深度的变量:

Variable: MATCHES_TREE_MAX_DEPTH (eg: 3)
Variable: MATCHES_TREE_ROOT_MAX_CHILDREN_AMOUNT (eg: 2)
Variable: MATCHES_TREE_PARENT_MAX_CHILDREN_AMOUNT (eg: 1)

这意味着我想创建一个深度为 3 的树结构(这包括根元素,所以我想要更深 2 级)。根元素有 2 个子元素,而子元素的任何子元素最多有 1 个子元素。

项目在我的用户数组中的顺序是我要在树中插入它们的顺序(我想先插入广度而不是深度)。

我在 SO 上找到了以下通用函数:PHP - 如何构建树结构列表? 但我似乎无法使其适应我的用例,因为我的数据库中没有父子关系。

这个 SO 答案返回给我一个看起来相似的数组,但是我在将其重新调整为我的用例时遇到了问题: PHP generate a tree by specified depth and rules

示例数据如下所示:

根用户:

object(stdClass)[56]
  public 'user_id' => string '1' (length=1)
  public 'first_name' => string 'Dennis' (length=6)

其他用户(儿童):

array (size=3)
  0 => 
    object(stdClass)[57]
      public 'user_id' => string '2' (length=2)
      public 'first_name' => string 'Tom' (length=3)
      public 'street' => string 'Teststreet' (length=10)
  1 => 
    object(stdClass)[58]
      public 'user_id' => string '3' (length=2)
      public 'first_name' => string 'Mary' (length=1)
      public 'street' => string 'Maryland avenue' (length=15)
  2 => 
    object(stdClass)[59]
      public 'user_id' => string '4' (length=2)
      public 'first_name' => string 'Jeff' (length=4)
      public 'street' => string 'Teststreet' (length=10)

我想通过填充示例实现的树的示例(考虑到 3 个最大深度、根的 2 个子项和除根以外的任何其他元素的 1 个最大子项的变量):

Array
(
    [userid] => 1
    [name] => "Dennis"
    [matches] => Array
        (
            [0] => Array
                (
                    [userid] => 2
                    [name] => "Tom"
                    [street] => "Teststreet"
                    [matches] => Array
                        (
                            [0] => Array
                                (
                                    [userid] => 4
                                    [name] => "Jeff"
                                    [street] => "Teststreet"
                                    [matches] = Array()
                                )
                        )
                )

            [1] => Array
                (
                    [userid] => 3
                    [name] => "Mary"
                    [street] => "Maryland avenue"
                    [matches] => Array
                        (
                        )
                )
        )
)

给定确定深度和子项的 3 个变量,如何创建此树结构?

标签: phptree

解决方案


编辑:我看到最初的问题是寻找一个适用于 3 个常量的解决方案。我已经添加到代码中,因此可以定义常量并基于它们进行循环,但我不确定我是否理解应该如何使用所有三个变量。特别是MATCHES_TREE_MAX_DEPTH根据您的描述似乎不合适。您只指出了两个级别的孩子,并指示我们应该跳过您列表中的任何其他项目。如果您想要定义更多子级的代码,则需要更清楚地了解您希望结构如何增长。

鉴于附加评论中对该问题描述的非常狭窄的限制,似乎浪费精力来处理循环来处理这个问题。这棵树的元素不能超过五个,因此像这样的迭代解决方案应该可以工作:

// function to convert from list objects to the array we want as output
function new_obj($obj) {
    $ret = array(
        "userid" => $obj->user_id,
        "name" => $obj->first_name
    );
    if (isset($obj->street)) {
        $ret["street"] = $obj->street;
    }
    $ret["matches"] = [];
    return $ret;
}


// INPUT DATA
$root = (object)["user_id" => "1", "first_name" => "Dennis"];
$children = [
    (object)[
        "user_id" => "2",
        "first_name" => "Tom",
        "street" => "Teststreet"
    ],
    (object)[
        "user_id" => "3",
        "first_name" => "Mary",
        "street" => "Maryland avenue"
    ],
    (object)[
        "user_id" => "4",
        "first_name" => "Jeff",
        "street" => "Teststreet"
    ],
    (object)[
        "user_id" => "5",
        "first_name" => "Arthur",
        "street" => "Teststreet"
    ]

];

$result1 = new_obj($root);

// an iterative solution, only works for one trivial set of values for the constants defined
// but also does provide some insight into a more general solution
if (isset($children[0])) {
    $result1["matches"][0] = new_obj($children[0]);
}
if (isset($children[1])) {
    $result1["matches"][1] = new_obj($children[1]);
}
if (isset($children[2])) {
    $result1["matches"][0]["matches"][0] = new_obj($children[2]);
}
if (isset($children[3])) {
    $result1["matches"][1]["matches"][0] = new_obj($children[3]);
}


print_r($result1);

如果您想定义常量/变量来指定子限制然后使用循环,请尝试使用上面定义的相同 $root 和 $children 变量。

// solution must use these constants:
define("MATCHES_TREE_MAX_DEPTH", 3);
define("MATCHES_TREE_ROOT_MAX_CHILDREN_AMOUNT", 2);
define("MATCHES_TREE_PARENT_MAX_CHILDREN_AMOUNT", 1);

$result2 = new_obj($root);

$i = 0;
while ($child = array_shift($children)) {
    $result2["matches"][$i] = new_obj($child);
    $i++;
    if ($i >= MATCHES_TREE_ROOT_MAX_CHILDREN_AMOUNT) break;
}

$i = 0;
while ($grandchild = array_shift($children)) {
    $child = $result2["matches"][$i];
    if (count($child["matches"]) >= MATCHES_TREE_PARENT_MAX_CHILDREN_AMOUNT) {
        // if we reach a child that has its max number of grandchildren, it's time to quit
        break;
    }
    // otherwise, assign this child as a grandchild
    $result2["matches"][$i]["matches"] = new_obj($grandchild);

    // increment the counter and check if we cycle back to the first child of root
    $i++;
    if ($i >= count($result2["matches"])) {
        $i = 0;
    }
}
print_r($result2);


推荐阅读