PHP构建树,但数组中有多个父级
我为寻找答案而痛苦了好几天,试图自己解决这个问题,但我做不到 我有一个PHP数组中的数据,其中的键PHP构建树,但数组中有多个父级,php,arrays,multidimensional-array,Php,Arrays,Multidimensional Array,我为寻找答案而痛苦了好几天,试图自己解决这个问题,但我做不到 我有一个PHP数组中的数据,其中的键parent\u id作为数组。我发现了如何构建一棵树,但前提是它只有一个父节点!但在我的例子中,它有多个父对象,并且必须嵌套在每个父对象的下面 以下是一个例子: 家长 数组( 'id'=>1, 'name'=>'Parent 1', 'parent_id'=>array() ); 数组( 'id'=>2, 'name'=>'Parent 2', 'parent_id'=>array() ); 儿童
parent\u id
作为数组。我发现了如何构建一棵树,但前提是它只有一个父节点!但在我的例子中,它有多个父对象,并且必须嵌套在每个父对象的下面
以下是一个例子:
家长
数组(
'id'=>1,
'name'=>'Parent 1',
'parent_id'=>array()
);代码>
数组(
'id'=>2,
'name'=>'Parent 2',
'parent_id'=>array()
);代码>
儿童
数组(
'id'=>3,
'name'=>'Child 1',
'parent_id'=>数组(1,2)
);代码>
我想把这棵树建成这样:
数组(
'id'=>1,
'name'=>'Parent 1',
'parent_id'=>array(),
'children'=>数组(
排列(
'id'=>3,
'name'=>'Child 1',
'parent_id'=>数组(1,2)
)
),
);
排列(
'id'=>2,
'name'=>'Parent 2',
'parent_id'=>array(),
'children'=>数组(
排列(
'id'=>3,
'name'=>'Child 1',
'parent_id'=>数组(1,2)
)
),
);代码>
你能推荐一个可以帮助我的功能吗。提前谢谢
已编辑()
@杰夫·兰伯特是对的。我所做的是在元素之间循环,如果有父元素,我将其ID添加到新创建的键children。。这样我就可以随时取回它
function build_tree(array $elements)
{
$indexed = array();
foreach($elements as $element)
{
$element = (array) $element;
$indexed[$element['id']] = $element;
}
$elements = $indexed;
unset($indexed, $element);
foreach($elements as $id => $element)
{
if ( ! empty($element['parent_id']))
{
foreach($element['parent_id'] as $parent)
{
if (isset($elements[$parent]))
{
$elements[$parent]['children'][] = $element['id'];
}
}
}
}
return $elements;
}
然后我只需要创建一个小函数来检索元素细节,如下所示:
function get_element($id, $return = NULL)
{
// Check the element inside the array
if (isset($elements[$id])
{
// In case I want to return a single value
if ($return !== NULL and isset($elements[$id][$return])
{
return $elements[$id][$return];
}
return $elements[$id];
}
return FALSE; // Or NULL, as you wish
}
这就是它正常工作的方式
$arr = array(
array('id'=>100, 'parentid'=>0, 'name'=>'a'),
array('id'=>101, 'parentid'=>100, 'name'=>'a'),
array('id'=>102, 'parentid'=>101, 'name'=>'a'),
array('id'=>103, 'parentid'=>101, 'name'=>'a'),
);
$new = array();
foreach ($arr as $a){
$new[$a['parentid']][] = $a;
}
$tree = createTree($new, array($arr[0]));
print_r($tree);
function createTree(&$list, $parent){
$tree = array();
foreach ($parent as $k=>$l){
if(isset($list[$l['id']])){
$l['children'] = createTree($list, $list[$l['id']]);
}
$tree[] = $l;
}
return $tree;
}
使用数组中的函数的解决方案:
// $parents and $children are arrays of 'parents' and 'children' items respectively
$tree = [];
foreach ($parents as $p) {
$treeItem = $p + ['children' => []];
foreach ($children as $c) {
if (in_array($p['id'], $c['parent_id']))
$treeItem['children'][] = $c;
}
$tree[] = $treeItem;
}
print_r($tree);
每个节点可以有多个父节点的树不是树,而是树。表示图形的一种方法是通过
实际上,您正在将每个节点的“子节点”存储在该节点索引中,而您不应该这样做,因为每个节点将与它连接到的其他节点重复相同的次数。每个节点都应该在结构的顶层表示,并包含对它们碰巧连接到的其他节点的引用,在您的'parent\u id'
索引中。我将分离出节点的实际定义,并在单独的结构中声明每个节点连接到的其他节点
以下是定义节点的方法:
array(
0 => array(
'id' => 1,
'name' => 'Parent 1',
),
1 => array(
'id' => 2,
'name' => 'Parent 2',
),
2 => array(
'id' => 3,
'name' => 'Child 1',
),
)
然后是一个单独的数组,看起来像这样,用于定义节点之间的连接:
array(
// These indices match the node indices above, and the values are the list of
// node indices each node has a connection to.
0 => array(2),
1 => array(2),
2 => array(0, 1),
)
然后就可以很容易地找到并实现您可能需要的任何类型的遍历算法。Update
如果希望/需要嵌套节点(例如父节点可以有一个或多个子节点,同时又是另一个父节点的子节点),则最简单的方法是为节点分配引用
我已经拼凑了一个与我最初的方法几乎相同的方法,除了它使用引用而不是按值赋值之外。代码如下所示:
function buildTree(array $data)
{
$data = array_column($data, null, 'id');
//reference to each node in loop
foreach ($data as &$node) {
if (!$node['parent_id']) {
//record has no parents - null or empty array
continue; //skip
}
foreach ($node['parent_id'] as $id) {
if (!isset($data[$id])) { // make sure parent exists
throw new \RuntimeException(
sprintf(
'Child id %d is orphaned, no parent %d found',
$node['id'], $id
)
);
}
if (!isset($data[$id]['children']) {
$data[$id]['children'] = array();
}
$data[$id]['children'][] = &$node; //assign a reference to the child node
}
}
return $data;
}
function buildTree(array $parents, array $children)
{
$parents = array_column($parents, null, 'id');
foreach ($children as $child) {
foreach ($child['parent_id'] as $id) {
if (!isset($parents[$id])) { // make sure parent exists
throw new \RuntimeException(
sprintf(
'Child id %d is orphaned, no parent %d found',
$child['id'], $id
)
);
}
if (!isset($parents[$id]['children']) {
$parents[$id]['children'] = array();
}
$parents[$id]['children'][] = $child;
}
}
return $parents;
}
此处需要双重引用,因为如果未使用foreach($data as&$node)
,则$node
变量将是原始节点的副本。指定副本的引用对您没有任何好处。事实上,这会产生错误的结果
同样,如果没有从循环中为和$node
分配引用,则无法获得整个树中的子节点的完整列表。
这不是最容易解释的事情,但最终结果不言而喻:使用此处的引用可以在单个函数调用中完整构建树
这是我要做的。首先,我将id用作数组键,这样我可以更容易地找到每个子项的父项:
$parents = array_column($parents, null, 'id');
如果您使用的是旧版本的PHP,并且无法升级,那么这相当于编写:
$indexed = array();
foreach ($parents as $parent) {
$indexed[$parent['id']] = $parent;
}
$parents = $indexed;
现在迭代这些子项,并将它们分配给它们的父母:
foreach ($children as $child) {
foreach ($child['parent_id'] as $id) {
if (!isset($parents[$id]['children']) {
$parents[$id]['children'] = array();//ensure the children key exists
}
$parents[$id]['children'][] = $child;//append child to parent
}
}
如果$parents
和$children
是两个独立的数组,或者这两个记录都在一个大数组中,这其实并不重要
因此,如果父级和子级位于单独的数组中,函数将如下所示:
function buildTree(array $data)
{
$data = array_column($data, null, 'id');
//reference to each node in loop
foreach ($data as &$node) {
if (!$node['parent_id']) {
//record has no parents - null or empty array
continue; //skip
}
foreach ($node['parent_id'] as $id) {
if (!isset($data[$id])) { // make sure parent exists
throw new \RuntimeException(
sprintf(
'Child id %d is orphaned, no parent %d found',
$node['id'], $id
)
);
}
if (!isset($data[$id]['children']) {
$data[$id]['children'] = array();
}
$data[$id]['children'][] = &$node; //assign a reference to the child node
}
}
return $data;
}
function buildTree(array $parents, array $children)
{
$parents = array_column($parents, null, 'id');
foreach ($children as $child) {
foreach ($child['parent_id'] as $id) {
if (!isset($parents[$id])) { // make sure parent exists
throw new \RuntimeException(
sprintf(
'Child id %d is orphaned, no parent %d found',
$child['id'], $id
)
);
}
if (!isset($parents[$id]['children']) {
$parents[$id]['children'] = array();
}
$parents[$id]['children'][] = $child;
}
}
return $parents;
}
如果所有数据都在一个数组中,则函数看起来几乎相同:
function buildTree(array $data)
{
$data = array_column($data, null, 'id');
foreach ($data as $node) {
if (!$node['parent_id']) {
//record has no parents - null or empty array
continue; //skip
}
foreach ($node['parent_id'] as $id) {
if (!isset($data[$id])) { // make sure parent exists
throw new \RuntimeException(
sprintf(
'Child id %d is orphaned, no parent %d found',
$node['id'], $id
)
);
}
if (!isset($data[$id]['children']) {
$data[$id]['children'] = array();
}
$data[$id]['children'][] = $node;
}
}
return $data;
}
您需要提供当前用于生成此树的代码。我以前使用过此代码,但它仅适用于单亲,而不是多亲。为什么要通过引用传递$list
?这是毫无意义的,而且在大多数情况下,由于PHP的“写时拷贝”机制,它实际上速度较慢(除非写入,否则不会复制该值。在这种情况下,您只能从$list
数组中读取,因此永远不会创建副本。您所做的只是增加引用计数。另外:请使用类型提示,并仔细查看OP的给定结构。我尝试了它,但仅适用于单亲!!但我有一个多父母的数组,因此每个父母下都应该列出孩子。这个同样适用@Elias!但是你能告诉我在哪里使用循环来深入吗?@KaderBouyakoub:“深入”是什么意思?您提供给我们的结构可以用这种方法处理,我看不到更多的层次。我的意思是多维数组,父级有子级,甚至子级也可能有子级。非常嵌套。(对不起我的英语)有时间我会看一看。使用递归很容易做到这一点,并首先处理有子元素的元素。这是最好且简单的解决方案!我编辑了我的问题并放置了我使用的函数。你能添加一些解释来描述这段代码如何回答这个问题吗?