PHP:从带有关联数据的数组创建多维数组

11

可能重复:
将基于父ID值的数组从单维转换为多维

我正在使用PHP。

我有一个具有关联数据(父子关系)的数组。

Array        
(        
    [5273] => Array        
        (        
            [id] => 5273        
            [name] => John Doe        
            [parent] =>         
        )        

    [6032] => Array        
        (        
            [id] => 6032        
            [name] => Sally Smith        
            [parent] => 5273        
        )        

    [6034] => Array        
        (        
            [id] => 6034        
            [name] => Mike Jones        
            [parent] => 6032        
        )        

    [6035] => Array        
        (        
            [id] => 6035        
            [name] => Jason Williams        
            [parent] => 6034        
        )        

    [6036] => Array        
        (        
            [id] => 6036        
            [name] => Sara Johnson        
            [parent] => 5273        
        )        

    [6037] => Array        
        (        
            [id] => 6037        
            [name] => Dave Wilson        
            [parent] => 5273        
        )        

    [6038] => Array        
        (        
            [id] => 6038        
            [name] => Amy Martin        
            [parent] => 6037        
        )        
)        
我需要它以这种JSON格式呈现:
{        
   "id":"5273",        
   "name":"John Doe",        
   "data":{        

   },        
   "children":[        
      {        
         "id":" Sally Smith",        
         "name":"6032",        
         "data":{        

         },        
         "children":[        
            {        
               "id":"6034",        
               "name":"Mike Jones",        
               "data":{        

               },        
               "children":[        
                  {        
                     "id":"6035",        
                     "name":"Jason Williams",        
                     "data":{        

                     },        
                     "children":[        
                        {        
                           "id":"node46",        
                           "name":"4.6",        
                           "data":{        

                           },        
                           "children":[        

                           ]        
                        }        
                     ]        
                  }        
               ]        
            },        
            {        
               "id":"6036",        
               "name":"Sara Johnson",        
               "data":{        

               },        
               "children":[        

               ]        
            },        
            {        
               "id":"6037",        
               "name":"Dave Wilson",        
               "data":{        

               },        
               "children":[        
                  {        
                     "id":"6038",        
                     "name":"Amy Martin",        
                     "data":{        

                     },        
                     "children":[        

                     ]        
                  }        
               ]        
            }        
         ]        
      }        
   ]        
}        
我知道我需要创建一个多维数组并将其运行通过json_encode()。我也相信这种方法需要使用递归,因为真实世界的数据可能有未知数量的级别。
我很乐意展示我的尝试,但它们都没有成功。
有人能帮帮我吗?
我被要求分享我的工作。这是我尝试过的,但我还没有接近我不知道它有多大帮助。
我只制作了一个关系数组。
foreach($array as $k => $v){
    $relationships[$v['id']] = $v['parent'];
}

我认为(基于另一个SO的帖子)使用这个关系数据来创建新的多维数组。如果我成功了,我将继续添加正确的“children”标签等。

$childrenTable = array();
    $data = array();
    foreach ($relationships as $n => $p) {
      //parent was not seen before, put on root
      if (!array_key_exists($p, $childrenTable)) {
          $childrenTable[$p] = array();
          $data[$p] = &$childrenTable[$p];  
      }
      //child was not seen before
      if (!array_key_exists($n, $childrenTable)) {
          $childrenTable[$n] = array();
      }
      //root node has a parent after all, relocate
      if (array_key_exists($n, $data)) {
          unset($data[$n]);
      }
      $childrenTable[$p][$n] = &$childrenTable[$n];      
    }
    unset($childrenTable);

print_r($data);

您发布的初始格式多维数组。在json编码中不应该可以工作吗? - Ben Roux
Ben Roux,是的,那是一个多维数组,但它的格式不正确,无法生成JSON。 - maestrojed
你尝试过什么?发布你的代码,说明你是如何准备数组的。 - Sanjay
Sanjay,我在我的问题中增加了一点我的工作内容。我无法在评论中正确地进行格式设置。我也尝试过do-while循环,但也失败了。 - maestrojed
Yoshi,我回去了并为所有以前的确定解决方案答案进行了归功。希望这有所帮助。我将在未来更好地跟进此事。 - maestrojed
3个回答

16
<?php
header('Content-Type: application/json; charset="utf-8"');

/**
 * Helper function
 * 
 * @param array   $d   flat data, implementing a id/parent id (adjacency list) structure
 * @param mixed   $r   root id, node to return
 * @param string  $pk  parent id index
 * @param string  $k   id index
 * @param string  $c   children index
 * @return array
 */
function makeRecursive($d, $r = 0, $pk = 'parent', $k = 'id', $c = 'children') {
  $m = array();
  foreach ($d as $e) {
    isset($m[$e[$pk]]) ?: $m[$e[$pk]] = array();
    isset($m[$e[$k]]) ?: $m[$e[$k]] = array();
    $m[$e[$pk]][] = array_merge($e, array($c => &$m[$e[$k]]));
  }

  return $m[$r][0]; // remove [0] if there could be more than one root nodes
}

echo json_encode(makeRecursive(array(
  array('id' => 5273, 'parent' => 0,    'name' => 'John Doe'),  
  array('id' => 6032, 'parent' => 5273, 'name' => 'Sally Smith'),
  array('id' => 6034, 'parent' => 6032, 'name' => 'Mike Jones'),
  array('id' => 6035, 'parent' => 6034, 'name' => 'Jason Williams'),
  array('id' => 6036, 'parent' => 5273, 'name' => 'Sara Johnson'),
  array('id' => 6037, 'parent' => 5273, 'name' => 'Dave Wilson'),
  array('id' => 6038, 'parent' => 6037, 'name' => 'Amy Martin'),
)));

演示:https://3v4l.org/s2PNC


1
那很有效,超出了我的预期。感谢您的帮助。我期待着分析它并学到一些东西! - maestrojed
1
@Yoshi,在进行了一些检查后,我发现如果您有多个_top level parents_,则应该return $m[$r]。无论如何,感谢您提供的解决方案。 - bumerang
@Yoshi,你的示例在单个父类别(父类别为0)上运行良好。但是当有多个父类别时,它就无法正常工作了。你能否给我一个解决方案? - Jimmy
@bumerang,是的,我从您的评论中得到了解决方案。谢谢。 - Jimmy
1
@Yoshi 懂了,谢谢兄弟。你的意思是这个(ref -&)将变量本身放在子部分中,而不是它的值的副本,在最终输出时获取其值。 - Lovepreet Singh Batth
显示剩余5条评论

3
好的,这就是它的工作原理,你一开始并没有太远的偏差,但你实际上要寻找的是引用。这是一个通用的过程:
由于父节点和子节点之间存在ID关系,所以您首先需要根据ID对数据进行索引。在这里,我使用数组($rows)来模拟您的数据访问,如果您从数据库中读取,那么它会类似。通过这种索引,您还可以添加其他属性,例如空数据。
// create an index on id
$index = array();
foreach($rows as $row)
{
    $row['data'] = (object) array();
    $index[$row['id']] = $row;
}

现在所有的条目都是按照它们的ID进行索引的。这是第一步。

第二步同样简单。因为我们现在可以根据$index中的ID访问每个节点,所以我们可以将子节点分配给它们的父节点。

有一个“虚拟”节点,它的ID为0。它不存在于任何行中,但是如果我们也能向它添加子节点,我们就可以使用这个子节点集合作为所有根节点的存储,如果是你的情况,则只有一个根节点。

当然,对于ID 0,我们不应该处理父节点——因为它不存在。

那么让我们这样做。我们在这里使用引用,因为否则同一个节点不能既是父节点又是子节点:

// build the tree
foreach($index as $id => &$row)
{
    if ($id === 0) continue;
    $parent = $row['parent'];
    $index[$parent]['children'][] = &$row;
}
unset($row);

因为我们使用引用,所以最后一行要在循环后取消存储在$row中的引用。
现在所有的子节点都已经分配到它们的父节点了。虽然这可能就是结果,但是让我们不要忘记最后一步,即需要访问实际的输出节点。
为了简洁起见,只需将根节点分配给$index本身。如果我们记得,我们想要的唯一根节点是具有ID 0的节点数组中的第一个节点:
// obtain root node
$index = $index[0]['children'][0];

就这样,我们现在可以直接使用它来生成JSON:

// output json
header('Content-Type: application/json');
echo json_encode($index);

最后,整个代码一览无余:
<?php
/**
 * @link https://dev59.com/8Wgu5IYBdhLWcg3wj3v5
 */

$rows = array(
    array('id' => 5273, 'parent' => 0,    'name' => 'John Doe'),
    array('id' => 6032, 'parent' => 5273, 'name' => 'Sally Smith'),
    array('id' => 6034, 'parent' => 6032, 'name' => 'Mike Jones'),
    array('id' => 6035, 'parent' => 6034, 'name' => 'Jason Williams'),
    array('id' => 6036, 'parent' => 5273, 'name' => 'Sara Johnson'),
    array('id' => 6037, 'parent' => 5273, 'name' => 'Dave Wilson'),
    array('id' => 6038, 'parent' => 6037, 'name' => 'Amy Martin'),
);

// create an index on id
$index = array();
foreach($rows as $row)
{
    $row['data'] = (object) [];
    $index[$row['id']] = $row;
}

// build the tree
foreach($index as $id => &$row)
{
    if ($id === 0) continue;
    $parent = $row['parent'];
    $index[$parent]['children'][] = &$row;
}
unset($row);

// obtain root node
$index = $index[0]['children'][0];

// output json
header('Content-Type: application/json');
echo json_encode($index, JSON_PRETTY_PRINT);

以下是使用PHP 5.4的JSON_PRETTY_PRINT生成的json:

{
    "id": 5273,
    "parent": 0,
    "name": "John Doe",
    "data": {

    },
    "children": [
        {
            "id": 6032,
            "parent": 5273,
            "name": "Sally Smith",
            "data": {

            },
            "children": [
                {
                    "id": 6034,
                    "parent": 6032,
                    "name": "Mike Jones",
                    "data": {

                    },
                    "children": [
                        {
                            "id": 6035,
                            "parent": 6034,
                            "name": "Jason Williams",
                            "data": {

                            }
                        }
                    ]
                }
            ]
        },
        {
            "id": 6036,
            "parent": 5273,
            "name": "Sara Johnson",
            "data": {

            }
        },
        {
            "id": 6037,
            "parent": 5273,
            "name": "Dave Wilson",
            "data": {

            },
            "children": [
                {
                    "id": 6038,
                    "parent": 6037,
                    "name": "Amy Martin",
                    "data": {

                    }
                }
            ]
        }
    ]
}

请问您能解释一下 unset($row) 的作用是什么吗? - Lovepreet Singh Batth
@LovepreetSinghBatth:在示例下面,它是这样解释的:“因为我们使用引用,所以最后一行负责在循环之后取消存储在 $row 中的引用。” - hakre

2
以下代码可以完成工作...根据您的需求可能需要进行一些微调。
$data = array(
    '5273' => array( 'id' =>5273, 'name'=> 'John Doe', 'parent'=>''),
    '6032' => array( 'id' =>6032, 'name'=> 'Sally Smith', 'parent'=>'5273'),
    '6034' => array( 'id' =>6034, 'name'=> 'Mike Jones ', 'parent'=>'6032'),
    '6035' => array( 'id' =>6035, 'name'=> 'Jason Williams', 'parent'=>'6034')
    );

$fdata = array();


function ConvertToMulti($data) {
    global $fdata;

    foreach($data as $k => $v)
    {
        if(empty($v['parent'])){
            unset($v['parent']);
        $v['data'] = array();
        $v['children'] = array();
            $fdata[] = $v;
        }
        else {
            findParentAndInsert($v, $fdata);
        }

    }
}

function findParentAndInsert($idata, &$ldata) {

    foreach ($ldata as $k=>$v) {

        if($ldata[$k]['id'] == $idata['parent']) {
            unset($idata['parent']);
        $idata['data'] = array();
        $idata['children'] = array();
            $ldata[$k]['children'][] = $idata;
            return;
        }
        else if(!empty($v['children']))
            findParentAndInsert($idata, $ldata[$k]['children']);
    }
}


print_r($data);
ConvertToMulti($data);
echo "AFTER\n";
print_r($fdata);

http://codepad.viper-7.com/Q5Buaz


我打算明天继续尝试。在第一次实现时似乎出现了问题。我还不确定是什么问题,将尝试在评论中提供详细信息。当我实现它时,没有出现错误,结构似乎正确,但是缺少很多数据。 - maestrojed
请注意以下几点:首先,请使用您自己的数据填充$data数组。它并不包含您在问题中使用的所有值。其次,您可以将$fdata设置为ConvertToMulti函数的本地变量。将其设置为全局变量可能会导致实现中出现问题。祝您实现成功! - FatalError

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接