在PHP业务开发中,我们经常会遇到需要对多维数组的每一行元素按照自定义键规则排序的场景,比如接口返回的数据需要按指定字段顺序输出,或者导出Excel时需要固定部分列的位置,其余列按特定模式排列。这时候就需要实现支持固定位置与模式匹配的键序控制的多维数组排序功能。

实现思路
要实现这个功能,核心逻辑可以分为三步:
- 首先定义排序规则,包含固定位置的键列表和模式匹配规则
- 然后对每一行数组的键进行拆分,分为固定位置键、匹配模式键、其余键三类
- 最后按照固定位置优先、模式匹配键次之、其余键按默认顺序排列的规则重组每一行的键顺序
核心实现函数
下面是实现该功能的完整函数,支持配置固定位置键和模式匹配规则:
<?php
/**
* 多维数组按自定义键规则逐行排序
* @param array $data 待排序的多维数组
* @param array $fixedKeys 固定位置的键列表,按所需顺序排列
* @param array $patternRules 模式匹配规则,格式为['模式' => 排序权重],权重越小越靠前
* @return array 排序后的多维数组
*/
function sortMultiArrayByCustomKey($data, $fixedKeys = [], $patternRules = []) {
if (empty($data) || !is_array($data)) {
return $data;
}
$result = [];
foreach ($data as $row) {
if (!is_array($row)) {
$result[] = $row;
continue;
}
$sortedRow = [];
$rowKeys = array_keys($row);
// 处理固定位置键
foreach ($fixedKeys as $fixedKey) {
if (in_array($fixedKey, $rowKeys)) {
$sortedRow[$fixedKey] = $row[$fixedKey];
// 移除已处理的键
$rowKeys = array_diff($rowKeys, [$fixedKey]);
}
}
// 处理模式匹配键
$patternKeyMap = [];
foreach ($rowKeys as $key) {
foreach ($patternRules as $pattern => $weight) {
if (preg_match($pattern, $key)) {
$patternKeyMap[$key] = $weight;
break;
}
}
}
// 按权重排序模式匹配键,权重相同按键名自然排序
uksort($patternKeyMap, function($a, $b) use ($patternKeyMap) {
if ($patternKeyMap[$a] == $patternKeyMap[$b]) {
return strnatcmp($a, $b);
}
return $patternKeyMap[$a] < $patternKeyMap[$b] ? -1 : 1;
});
foreach (array_keys($patternKeyMap) as $patternKey) {
$sortedRow[$patternKey] = $row[$patternKey];
$rowKeys = array_diff($rowKeys, [$patternKey]);
}
// 处理剩余键,按自然排序
sort($rowKeys, SORT_NATURAL);
foreach ($rowKeys as $leftKey) {
$sortedRow[$leftKey] = $row[$leftKey];
}
$result[] = $sortedRow;
}
return $result;
}
?>
参数说明
| 参数名 | 类型 | 说明 |
|---|---|---|
| data | array | 待排序的多维数组,每一行必须是键值对数组 |
| fixedKeys | array | 固定位置的键列表,数组元素顺序即为排序后的键顺序,不存在的键会自动忽略 |
| patternRules | array | 模式匹配规则,键为正则表达式,值为排序权重,权重越小排序越靠前 |
使用示例
下面是一个实际的使用案例,假设我们有用户数据,需要把id、name放在最前面,以time结尾的键放在中间,其余键放在最后:
<?php
// 测试数据
$userData = [
[
'age' => 25,
'name' => '张三',
'create_time' => '2024-01-01',
'id' => 1,
'update_time' => '2024-01-02',
'email' => 'test@ipipp.com'
],
[
'login_time' => '2024-01-03',
'id' => 2,
'name' => '李四',
'age' => 30,
'email' => 'test2@ipipp.com'
]
];
// 固定位置键:id、name排在最前
$fixedKeys = ['id', 'name'];
// 模式规则:以time结尾的键权重为1,排在第二位
$patternRules = ['/time$/' => 1];
$sortedData = sortMultiArrayByCustomKey($userData, $fixedKeys, $patternRules);
print_r($sortedData);
?>
输出结果说明
上述代码的输出结果中,每一行数组的键顺序都会符合我们的规则:
- 第一位是id,第二位是name,这两个是固定位置的键
- 接下来是create_time、update_time、login_time这类以time结尾的键,按自然顺序排列
- 最后是age、email这类既不在固定位置也不匹配模式的键,按自然顺序排列
注意事项
- 固定位置键如果某一行不存在,会自动跳过,不会报错
- 模式匹配使用的是正则表达式,需要注意正则语法的正确性
- 如果同一个键同时匹配多个模式规则,只会按第一个匹配到的规则计算权重
- 该函数只处理二维数组的逐行排序,更深层的多维数组需要递归处理