话不多说,直接说重点。最近用到了省市区数据联动。数据格式是这样的:
$array = [
0 => [
'id' => 1,
'pid' => 0,
'name' => 't1',
],
1 => [
'id' => 2,
'pid' => 0,
'name' => 't2',
],
2 => [
'id' => 3,
'pid' => 0,
'name' => 't3',
],
3 => [
'id' => 4,
'pid' => 1,
'name' => 't1-t4',
],
4 => [
'id' => 5,
'pid' => 2,
'name' => 't2-t5',
],
5 => [
'id' => 6,
'pid' => 4,
'name' => 't1-t4-t6',
],
];
返回格式要求是这样的:
Array ( [0] => Array ( [id] => 1 [pid] => 0 [name] => t1 [child] => Array ( [0] => Array ( [id] => 4 [pid] => 1 [name] => t1-t4 [child] => Array ( [0] => Array ( [id] => 6 [pid] => 4 [name] => t1-t4-t6 ) ) ) ) ) [1] => Array ( [id] => 2 [pid] => 0 [name] => t2 [child] => Array ( [0] => Array ( [id] => 5 [pid] => 2 [name] => t2-t5 ) ) ) [2] => Array ( [id] => 3 [pid] => 0 [name] => t3 ) )
在知道数组层级且层级比较少的情况下,最简单粗暴的方式当然是多层遍历组装数组。但是这样显然有点 low。来说三种比较洋气的方式。
递归
首先想到的当然是递归。递归的本质也是遍历循环,效率应该相差不多,但是代码量明显减少,看起来清爽了很多。
function getOptionsTree($array, $pid = 0) { $optionsTree = []; foreach ($array as $key => $item){ //假设从根节点开始,pid 为0,将根节点元素放入数组 if ($item['pid'] == $pid){ $optionsTree[$item['id']] = $item; //销毁已查询的,减轻下次递归查询数量 unset($array[$key]); //递归开始,查询根节点的子类,并将其元素放入根节点下的 child 中 $optionsTree[$item['id']]['child'] = $this->getOptionsTree($array, $item['id']); } } return $optionsTree; }
栈递归
该方法暂时只能处理2层数据,后期待优化。但是重点不是这个方法,而是利用栈来解决问题的思路。该方法利用了栈 先进后出,即后进先出 的特点,通过入栈、出栈来达到数据组合的目的。
function getTreeOptions($array, $pid = 0) { $tree = []; if (!empty($array)) { //先取出顶级的来压入数组$stack中,并将在$list中的删除掉 $stack = []; foreach ($array as $key => $value) { if ($value['pid'] == $pid) { array_push($stack,$value); unset($array[$key]); } } while (count($stack)) { //先从栈中取出第一项 $info = array_pop($stack); if ($info['pid'] == 0){ $tree[$info['id']] = $info; }else{ $tree[$info['pid']]['child'][$info['id']] = $info; } //查询剩余的$list中pid与其id相等的,也就是查找其子节点 foreach ($array as $key => $child) { if ($child['pid'] == $info['id']) { //如果有子节点则入栈,while循环中会继续查找子节点的下级 array_push($stack, $child); unset($array[$key]); } } } } return $tree; }
引用
首推方法。引用赋值,就是说将赋值左边的直接指向内存中存储这个值的这块区域,如 $b = &$a,$a 和 $b 在内存中指向的值的区域是相同的,因此当这块区域的值被改变,两个变量都会被改变。
明白这一点,这个方法也就可以看明白了。
function getTreeOptions($list, $pid = 0) { $tree = []; if (!empty($list)) { //先修改为以id为下标的列表 $newList = []; foreach ($list as $k => $v) { $newList[$v['id']] = $v; } //然后开始组装成特殊格式 foreach ($newList as $value) { if ($pid == $value['pid']) {//先取出顶级 $tree[] = &$newList[$value['id']]; } elseif (isset($newList[$value['pid']])) {//再判定非顶级的pid是否存在,如果存在,则再pid所在的数组下面加入一个字段items,来将本身存进去 $newList[$value['pid']]['child'][] = &$newList[$value['id']]; } } } return $tree; }
好文章要分享: