樂猪先生

PHP多维数组排序之usort

2017-08-18

在最近对接一个艺龙的接口的项目中需要对数据进行去重操作,保留最新的数据。第一想到的就是先对数组根据时间字段来进行升序排列。但是在这篇文章中,为了演示和实验我先使用了TotalPrice来排序,效果都是一样的。所以开始下面的代码

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//艺龙返回的原始数据,已经整理了格式
$hotelList => [
[
"LastId" => 32714,
"Time" => "2017-07-24T10:36:33+08:00",
"OrderId" => 171868350,
"TotalPrice" => 200,
],
[
"LastId" => 32731,
"Time" => "2017-07-24T00:38:00+08:00",
"OrderId" => 171868367,
"TotalPrice" => 200,
],
[
"LastId" => 32733,
"Time" => "2017-07-24T09:20:00+08:00",
"OrderId" => 171868366,
"TotalPrice" => 200,
],
[
"LastId" => 32749,
"Time" => "2017-07-24T01:35:25+08:00",
"OrderId" => 171868375,
"TotalPrice" => 400,
],
[
"LastId" => 32757,
"Time" => "2017-07-24T01:36:00+08:00",
"OrderId" => 171868383,
"TotalPrice" => 160,
],
[
"LastId" => 32769,
"Time" => "2017-07-24T01:36:59+08:00",
"OrderId" => 171868395,
"TotalPrice" => 150,
],
[
"LastId" => 32778,
"Time" => "2017-07-24T01:38:09+08:00",
"OrderId" => 171868405,
"TotalPrice" => 700,
],
];
1
2
3
4
5
6
7
usort($incrOrderList, function($a, $b) {
if ($a['TotalPrice'] == $b['TotalPrice']) {
return 0;
}
return ($a['TotalPrice'] < $b['TotalPrice']) ? -1 : 1; //正序
});
dd($incrOrderList)

那么输出的结果为

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
array:7 [▼
0 => array:5 [▼
"LastId" => 32769
"Time" => "2017-07-24T01:36:59+08:00"
"OrderId" => 171868395
"TotalPrice" => 150
]
1 => array:5 [▼
"LastId" => 32757
"Time" => "2017-07-24T01:36:00+08:00"
"OrderId" => 171868383
"TotalPrice" => 160
]
2 => array:5 [▼
"LastId" => 32714
"Time" => "2017-07-24T10:36:33+08:00"
"OrderId" => 171868350
"TotalPrice" => 200
]
3 => array:5 [▼
"LastId" => 32731
"Time" => "2017-07-24T00:38:00+08:00"
"OrderId" => 171868367
"TotalPrice" => 200
]
4 => array:5 [▼
"LastId" => 32733
"Time" => "2017-07-24T09:20:00+08:00"
"OrderId" => 171868366
"TotalPrice" => 200
]
5 => array:5 [▼
"LastId" => 32749
"Time" => "2017-07-24T01:35:25+08:00"
"OrderId" => 171868375
"TotalPrice" => 400
]
6 => array:5 [▼
"LastId" => 32778
"Time" => "2017-07-24T01:38:09+08:00"
"OrderId" => 171868405
"TotalPrice" => 700
]
]

可以看到所有的元素都以TotalPrice字段进行正序排列。如果想要倒序的话只需要在上面的匿名函数里将-11调换位置即可。如果是php7那么下面的写法将更加的简洁。

1
2
3
usort($hotelList, function ($a, $b) {
return $a['TotalPrice'] <=> $b['TotalPrice']; //太空船运算符(php7+)
});

到此已经满足了我的排序需求。


可以看出这和MySQL的ORDER BY很像。那么这个时候MySQL的ORDER BY可以同时对多个字段进行排序,如:ORDER BY TotalPrice ASC,Time DESC先对TotalPrice进行正序然后再对TotalPrice值相同的字段进行Time的倒序排列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
test> SELECT `TotalPrice`,`Time` from tmp order by `TotalPrice` asc;
+--------------+---------------------+
| TotalPrice | Time |
|--------------+---------------------|
| 150 | 2017-07-24 01:36:59 |
| 160 | 2017-07-24 01:36:00 |
| 200 | 2017-07-24 10:36:33 |
| 200 | 2017-07-24 00:38:00 |
| 200 | 2017-07-24 09:20:00 |
| 400 | 2017-07-24 01:35:25 |
| 700 | 2017-07-24 01:38:09 |
+--------------+---------------------+
test> SELECT `TotalPrice`,`Time` from tmp order by `TotalPrice` asc,`Time` desc;
+--------------+---------------------+
| TotalPrice | Time |
|--------------+---------------------|
| 150 | 2017-07-24 01:36:59 |
| 160 | 2017-07-24 01:36:00 |
| 200 | 2017-07-24 10:36:33 |
| 200 | 2017-07-24 09:20:00 |
| 200 | 2017-07-24 00:38:00 |
| 400 | 2017-07-24 01:35:25 |
| 700 | 2017-07-24 01:38:09 |
+--------------+---------------------+

那么我们通过 usort也可以实现类似的效果。直接上代码

1
2
3
4
5
6
7
8
9
10
11
12
13
usort($hotelList, function ($a, $b) {
$criteria = [
'TotalPrice' => 'asc',
'Time' => 'desc' //这里还可以根据需要继续加条件 如:'x'=>'asc'等
];
foreach ($criteria as $field => $order) {
if ($a[$field] == $b[$field]) {
continue;
}
return (($order == 'desc') ? -1 : 1) * (($a[$field] < $b[$field]) ? -1 : 1);
}
return 0;
});

输出结果为

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
array:7 [▼
0 => array:5 [▼
"LastId" => 32769
"Time" => "2017-07-24T01:36:59+08:00"
"OrderId" => 171868395
"TotalPrice" => 150
]
1 => array:5 [▼
"LastId" => 32757
"Time" => "2017-07-24T01:36:00+08:00"
"OrderId" => 171868383
"TotalPrice" => 160
]
2 => array:5 [▼
"LastId" => 32714
"Time" => "2017-07-24T10:36:33+08:00"
"OrderId" => 171868350
"TotalPrice" => 200
]
3 => array:5 [▼
"LastId" => 32733
"Time" => "2017-07-24T09:20:00+08:00"
"OrderId" => 171868366
"TotalPrice" => 200
]
4 => array:5 [▼
"LastId" => 32731
"Time" => "2017-07-24T00:38:00+08:00"
"OrderId" => 171868367
"TotalPrice" => 200
]
5 => array:5 [▼
"LastId" => 32749
"Time" => "2017-07-24T01:35:25+08:00"
"OrderId" => 171868375
"TotalPrice" => 400
]
6 => array:5 [▼
"LastId" => 32778
"Time" => "2017-07-24T01:38:09+08:00"
"OrderId" => 171868405
"TotalPrice" => 700
]
]

重点关注TotalPrice为200的元素,元素的顺序已经按照Time字段进行倒序排列了。和我们上面查询的MySQL结果是一致的。
后面的多个字段排序方法参考了CSDN博客。对于usort已经有了大概的了解,但是对于具体的排序原理还不是很清楚。如果是2个元素的排序很好理解,当时多于2个的时候就有些不理解。


2017-08-16更新
在上面对2个字段同时进行排序的时候,看了别人的代码写的有些难理解。今天在StackOverFlow看到另一种写法,很容易理解。代码如下:

1
2
3
4
5
usort($hotelList, function ($a, $b) {
return
($a['TotalPrice'] - $b['TotalPrice']) ?:
(strtotime($b['Time']) - strtotime($a['Time'])); //Time倒序则$b放在前面
});

由于Time字段做减法运算会报一个不是数字类型的错误,所以我转了一下。得到的结果和上面是一样的。而这样的写法看起来更加的舒服。这个写法需要注意的一点是如果是正序则$a则放在前面,$b放在后面进行减法运算

扫描二维码,分享此文章