PHP 分层数组 - 父母和孩子
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13877656/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
PHP hierarchical array - Parents and childs
提问by Jens T?rnell
I use PHP and mySQL with Idiorm. That might not be relevant.
我将 PHP 和 mySQL 与Idiorm一起使用。那可能不相关。
My PHP array
我的 PHP 数组
- It's a relationship between parents and childs.
- 0 is the root parent.
- Example: Root parent 0 have the child 33 which have the child 27 which have the child 71.
- 这是父母和孩子之间的关系。
- 0 是根父级。
- 示例:根父级 0 有子级 33,子级 27 有子级 71。
This array structure can be changed if needed for solving the problem.
如果需要解决问题,可以更改此数组结构。
array (
33 =>
array (
0 => '27',
1 => '41',
),
27 =>
array (
0 => '64',
1 => '71',
),
0 =>
array (
0 => '28',
1 => '29',
2 => '33',
),
)
My hierarchical result
我的分层结果
Something like this, but as an array...
像这样的东西,但作为一个数组......
0 =>
28
29
33
27 =>
64
71
41
Information
信息
- The depth are unkown and it can be unlimited. I tried foreach, but it might not be the way.
- 深度是未知的,它可以是无限的。我试过 foreach,但可能不是这样。
My own thoughts
我自己的想法
- Some recursive function?
- Some while loops?
- 一些递归函数?
- 一些while循环?
I tried both of the above, just got a mess. It's a brainer.
上面两种方法我都试过了,只是一团糟。这是一个脑洞。
回答by Jens T?rnell
The suggestion by @deceze worked. However the input array needs to change a litte, like this...
@deceze 的建议奏效了。但是输入数组需要改变一点,像这样......
$rows = array(
array(
'id' => 33,
'parent_id' => 0,
),
array(
'id' => 34,
'parent_id' => 0,
),
array(
'id' => 27,
'parent_id' => 33,
),
array(
'id' => 17,
'parent_id' => 27,
),
);
From https://stackoverflow.com/a/8587437/476:
从https://stackoverflow.com/a/8587437/476:
function buildTree(array $elements, $parentId = 0) {
$branch = array();
foreach ($elements as $element) {
if ($element['parent_id'] == $parentId) {
$children = buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
$tree = buildTree($rows);
print_r( $tree );
回答by Fariz Luqman
I added to @Jens T?rnell's answers to enable defining the options for the column name of parent_id, the children array key name, and also the column name for id.
我添加到@Jens T?rnell 的答案中,以启用定义 parent_id 列名、子数组键名以及 id 列名的选项。
/**
* function buildTree
* @param array $elements
* @param array $options['parent_id_column_name', 'children_key_name', 'id_column_name']
* @param int $parentId
* @return array
*/
function buildTree(array $elements, $options = [
'parent_id_column_name' => 'parent_id',
'children_key_name' => 'children',
'id_column_name' => 'id'], $parentId = 0)
{
$branch = array();
foreach ($elements as $element) {
if ($element[$options['parent_id_column_name']] == $parentId) {
$children = buildTree($elements, $options, $element[$options['id_column_name']]);
if ($children) {
$element[$options['children_key_name']] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
Since the functionality is quite universal, I managed to use the above function in most of my projects.
由于功能非常通用,我设法在我的大多数项目中使用了上述功能。
回答by Danish
great answer from @Jens T?rnell, just wanted to add a little improvement that if your parent_id and id is actually string instead of number then above method will fail and after creating children array, it will create those childrens arrays again as separate individual array. In order to fix that you should do triple equal check and by telling data type of variable i.e (string) in comparison.
@Jens T?rnell 的好答案,只是想添加一点改进,如果您的 parent_id 和 id 实际上是字符串而不是数字,那么上述方法将失败,并且在创建子数组后,它将再次创建这些子数组作为单独的单个数组. 为了解决这个问题,您应该进行三重相等检查并通过比较变量 ie (string) 的数据类型。
For string based Id and Parent_id in array
对于数组中基于字符串的 Id 和 Parent_id
function buildTree(array $elements, $parentId = 0) {
$branch = array();
foreach ($elements as $element) {
if ((string)$element['parent_id'] === (string)$parentId) {
$children = buildTree($elements, $element['id']);
if ($children) {
$element['children'] = $children;
}
$branch[] = $element;
}
}
return $branch;
}
additionally if someone desire, he can add a third parameter to function as well to specify data type of variables dynamically i.e function buildTree(array $elements, $parentId = 0, $datatype='string')but then you will have to take of any other error occur.
此外,如果有人愿意,他还可以添加第三个参数来运行,以动态指定变量的数据类型,即,function buildTree(array $elements, $parentId = 0, $datatype='string')但是您将不得不处理发生的任何其他错误。
hope it will help someone!
希望它会帮助某人!
回答by Asad Iftikhar
public function createTree (&$list, $parentId = null) {
$tree = array();
foreach ($list as $key => $eachNode) {
if ($eachNode['parentId'] == $parentId) {
$eachNode['children'] = $this->createTree ($list,$eachNode['id']);
$tree[] = $eachNode;
unset($list[$key]);
}
}
return $tree;
}
In that function pass the associative array and if the most parent is not null then just pass the most parent id as second argument.
在该函数中传递关联数组,如果最父不为空,则只需将最父 id 作为第二个参数传递。
回答by Guy Thomas
I had a different problem and could not find a solution that worked for me on this page. I needed to create a tree but without knowing the root id. This means I have to go through my flat array and build branches with the most parently items at the top of the tree.
我遇到了一个不同的问题,在此页面上找不到适合我的解决方案。我需要创建一棵树,但不知道根 id。这意味着我必须遍历我的平面数组,并在树的顶部使用最多的父项构建分支。
If anyone else needs to build a tree without a root parent item id, here's how I did it.
如果其他人需要在没有根父项 ID 的情况下构建一棵树,我就是这样做的。
<?php
$rows = [
(object) [
'id' => 1001,
'parentid' => 1000,
'name' => 'test1.1'
],
(object) [
'id' => 1000,
'parentid' => 100,
'name' => 'test1'
],
(object) [
'id' => 1002,
'parentid' => 1000,
'name' => 'test1.2'
],
(object) [
'id' => 1004,
'parentid' => 1001,
'name' => 'test1.1.1'
],
(object) [
'id' => 1005,
'parentid' => 1004,
'name' => 'test1.1.1.1'
],
(object) [
'id' => 100,
'parentid' => 10,
'name' => 'test 0'
],
(object) [
'id' => 1006,
'parentid' => 1002,
'name' => 'test1.2.1'
],
(object) [
'id' => 1007,
'parentid' => 1002,
'name' => 'test1.2.2'
],
];
function add_child(stdClass $parent, stdClass $child) {
if ($child->parentid != $parent->id) {
throw new Exception('Attempting to add child to wrong parent');
}
if (empty($parent->children)) {
$parent->children = [];
} else {
// Deal where already in branch.
foreach ($parent->children as $idx => $chd) {
if ($chd->id === $child->id) {
if (empty($chd->children)) {
// Go with $child, since $chd has no children.
$parent->children[$idx] = $child;
return;
} else {
if (empty($child->children)) {
// Already has this child with children.
// Nothing to do.
return;
} else {
// Both childs have children - merge them.
$chd->children += $child->children;
$parent->children[$idx] = $child;
return;
}
}
}
}
}
$parent->children[] = $child;
}
function build_branch(&$branch, &$rows, &$parent = null) {
$hitbottom = false;
while (!$hitbottom) {
$foundsomething = false;
// Pass 1 - find children.
$removals = []; // Indexes of rows to remove after this loop.
foreach ($rows as $idx => $row) {
if ($row->parentid === $branch->id) {
// Found a child.
$foundsomething = true;
// Recurse - find children of this child.
build_branch($row, $rows, $branch);
add_child($branch, $row);
$removals[] = $idx;
}
}
foreach ($removals as $idx) {
unset($rows[$idx]);
}
// Pass 2 - find parents.
if ($parent === null) {
$foundparent = false;
foreach ($rows as $idx => $row) {
if ($row->id === $branch->parentid) {
// Found parent
$foundsomething = true;
$foundparent = true;
add_child($row, $branch);
unset ($rows[$idx]);
// Now the branch needs to become the parent since parent contains branch.
$branch = $row;
// No need to search for other parents of this branch.
break;
}
}
}
$hitbottom = !$foundsomething;
}
}
function build_tree(array $rows) {
$tree = [];
while (!empty($rows)) {
$row = array_shift($rows);
build_branch($row, $rows);
$tree[] = $row;
}
return $tree;
}
$tree = build_tree($rows);
print_r($tree);

