七零部落格
思则大道至简,疑则谜团重重!
思则大道至简,疑则谜团重重!
我们知道很多开源软件的无限分类都是采用递归的算法,但是我们知道递归即浪费时间,又浪费空间(内存),实践中,我们一般会在model中查询出格式化成主键值对应数据的形式,因而我们可以直接用这样的数据,就少了一层循环。代码也非常简洁。
先来看看分类方法:arr.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
<?php
/**
* 方法一:
* 此方法由@Tonton 提供
* @form http://levi.cg.am
* @date 2012-12-12
*/
function genTree5(Array $items ) {
foreach ( $items as $item )
$items [ $item [ 'pid' ]][ 'son' ][ $item [ 'id' ]] = & $items [ $item [ 'id' ]];
return isset( $items [0][ 'son' ]) ? $items [0][ 'son' ] : array ();
}
/**
* 方法二:将数据格式化成树形结构
* @author Xuefen.Tong
* @form http://levi.cg.am
* @param array $items
* @return array
*/
function genTree9(Array $items ) {
$tree = array (); //格式化好的树
foreach ( $items as $item )
if (isset( $items [ $item [ 'pid' ]]))
$items [ $item [ 'pid' ]][ 'son' ][] = & $items [ $item [ 'id' ]];
else
$tree [] = & $items [ $item [ 'id' ]];
return $tree ;
}
|
试着输出看看ex1.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
<?php
include 'arr.php' ;
$items = array (
1 => array ( 'id' => 1, 'pid' => 0, 'name' => '江西省' ),
2 => array ( 'id' => 2, 'pid' => 0, 'name' => '黑龙江省' ),
3 => array ( 'id' => 3, 'pid' => 1, 'name' => '南昌市' ),
4 => array ( 'id' => 4, 'pid' => 2, 'name' => '哈尔滨市' ),
5 => array ( 'id' => 5, 'pid' => 2, 'name' => '鸡西市' ),
6 => array ( 'id' => 6, 'pid' => 4, 'name' => '香坊区' ),
7 => array ( 'id' => 7, 'pid' => 4, 'name' => '南岗区' ),
8 => array ( 'id' => 8, 'pid' => 6, 'name' => '和兴路' ),
9 => array ( 'id' => 9, 'pid' => 7, 'name' => '西大直街' ),
10 => array ( 'id' => 10, 'pid' => 8, 'name' => '东北林业大学' ),
11 => array ( 'id' => 11, 'pid' => 9, 'name' => '哈尔滨工业大学' ),
12 => array ( 'id' => 12, 'pid' => 8, 'name' => '哈尔滨师范大学' ),
13 => array ( 'id' => 13, 'pid' => 1, 'name' => '赣州市' ),
14 => array ( 'id' => 14, 'pid' => 13, 'name' => '赣县' ),
15 => array ( 'id' => 15, 'pid' => 13, 'name' => '于都县' ),
16 => array ( 'id' => 16, 'pid' => 14, 'name' => '茅店镇' ),
17 => array ( 'id' => 17, 'pid' => 14, 'name' => '大田乡' ),
18 => array ( 'id' => 18, 'pid' => 16, 'name' => '义源村' ),
19 => array ( 'id' => 19, 'pid' => 16, 'name' => '上坝村' ),
);
header( "Content-Type: text/html; charset=utf-8" );
echo "<pre>" ;
print_r(genTree5( $items ));
print_r(genTree9( $items ));
echo "</pre>" ;
|
打印结果太长,请自行测试:
原本写到这里就基本差不多了,但是最近有人一直在问这个代码有BUG,于是做几个解答:
从上面代码看出,要分类得数据是要按照严格规范来进行,如下:
我之前有发表过一篇文章专门讲解无限分类的原理,大家如果觉得困惑的话,请移步到这里:
这应该是质疑声最多的话了,这样的话明显带有几个错误:
那么怎样才能使自己的数据按照规范提供:
sql按照关键ID查询,例如:
1
2
3
4
5
6
7
8
9
10
11
12
|
$data = array ();
$result = mysqli::query( 'SELECT `id`, `pid`, `name` FROM `table`;' );
if ( $result ->num_rows)
{
while (FALSE != ( $row = $this ->fetch_assoc()))
{
$id = $row [ 'id' ];
$data [ $id ] = $row ;
}
}
$data = genTree5( $data );
|
非sql查询呢,那么你可以自己创建一个索引出来,如下:
1
2
3
4
5
6
7
8
|
$data = array ();
foreach ( $result as $value )
{
$id = $value [ 'id' ];
$data [ $id ] = $value ;
}
$data = genTree5( $data );
|
比如上面的排序都使按照id来的,这违背了我的排序的意图。看到有朋友这样留言,其实这也是对分类方法进行了误解,我想说明的是:
这个方法只提供了分类,他单纯的为分类而产生,而不是排序,排序是排序,分类是分类,请分开理解。
ok,那如何来排序呢?比如我希望黑龙江在江西上面,赣州又在南昌上面,诸如此类等。刚才也提到了,分类和排序是两个概念,既然这里多了一个分类的“元素”,再来看上面的数据规范,那么应当加上一个新的规范条件:
回到上面城市示例:我希望黑龙江在江西上面,赣州又在南昌上面,怎么做?
SQL查询版:
1
2
3
4
5
6
7
8
9
10
11
12
|
$data = array ();
$result = mysqli::query( 'SELECT `id`, `pid`, `name` FROM `table` ORDER BY `num` ASC;' );
if ( $result ->num_rows)
{
while (FALSE != ( $row = $this ->fetch_assoc()))
{
$id = $row [ 'id' ];
$data [ $id ] = $row ;
}
}
$data = genTree5( $data );
|
非SQL查询,可以通过php二维排序:
array_multisort的方法以前有提过了,这里提供uasort的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
class ArrSort
{
private $_name ;
public function __construct( $name )
{
$this ->_name = $name ;
}
public function num( $x , $y )
{
return (Int) $x [ $this ->_name] > (Int) $y [ $this ->_name];
}
private function str( $x , $y )
{
return strcasecmp ( $x [ $this ->_name], $y [ $this ->_name]);
}
};
// 根据数字进行排序
uasort( $data , array ( new ArrSort( 'num' ), 'num' ));
// 根据字符进行排序
uasort( $data , array ( new ArrSort( 'char' ), 'str' ));
|
我们增加点难度,比如上海,我们想要搞特殊,把他单独提升到第一位,怎么做呢?
代码如下:
1
2
3
4
5
6
7
8
9
10
11
|
$data = array ();
foreach ( $result as $key => $val )
{
if ( $val [ 'name' ] == '上海' )
{
$data [ $key ] = $val ;
unset( $result [ $key ]);
}
}
$data = array_merge ( $data , $result );
|
如果大家有兴趣,自己可以试着想想把上海放在中间特定某一个位置怎么做,把上海放在末位又该怎么做。
以上只是列举了一小部分,方法有很多,大家自己去发掘。上面这个分类的方法还是非常好用的,他让我们告别了繁琐的递归,提高了我们代码执行效率,方法已提供了,不能因为不会用就说这个方法是有bug。这就好比提供给你碗和筷子了,但是饭还是要自己盛来吃的嘛。