PHP 按优先级排序数组
PHP sort array by priority
我需要一种按优先级对项目数组进行排序的方法。
这是我目前所做的工作:
function arraySortPriority(array &$array, $offset, array $priorities)
{
uasort($array, function ($a, $b) use ($offset, $priorities) {
if (!isset($a[$offset])) {
$a[$offset] = null;
}
if (!isset($b[$offset])) {
$b[$offset] = null;
}
if ($a[$offset] == $b[$offset]) {
return 0;
}
$aPriority = isset($priorities[$a[$offset]])
? $priorities[$a[$offset]]
: null;
$bPriority = isset($priorities[$b[$offset]])
? $priorities[$b[$offset]]
: null;
return $aPriority > $bPriority ? -1 : 1;
});
}
// an array to sort
$array = [
['type' => 'A'],
['type' => 'A'],
['type' => 'B'],
['type' => 'B'],
['type' => 'C'],
['type' => 'C'],
['type' => 'D'],
['type' => 'D'],
['type' => 'E'],
['type' => 'E'],
['type' => 'F'],
['type' => 'F'],
['type' => 'G'],
['type' => 'G'],
['type' => 'H'],
['type' => 'H'],
['type' => 'Foo'],
['type' => 'Foo'],
['type' => 'Bar'],
['type' => 'Bar'],
[0 => 'no type should be last'],
[0 => 'no type should be last'],
];
// shuffle the array
shuffle($array);
// set priorities
$priorities = [
'A' => 8,
'B' => 7,
'C' => 6,
'D' => 5,
'E' => 4,
'F' => 3,
'G' => 2,
'H' => 1,
];
// call
arraySortPriority($array, 'type', $priorities);
// test output
foreach ($array as $item) {
if (isset($item['type'])) {
echo "{$item['type']}\r\n";
} else {
$values = array_values($item);
echo reset($values) . PHP_EOL;
}
}
预期:
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
Foo
Foo
Bar
Bar
no type should be last
no type should be last
实际:
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
no type should be last <-- should be at bottom
no type should be last <-- should be at bottom
Bar
Bar
Foo
Foo
问题是没有给予 $offset
的项目应该总是排在底部。
这意味着 no type should be last
的排序应始终低于 Foo
或 Bar
。
我该怎么做?
您没有处理两个优先级相同的情况。
解决方案 1
只需将以下内容添加到您的函数中的三元前:
if ($aPriority == $bPriority)
return 0;
所以它应该是这样的:
uasort($array, function ($a, $b) use ($offset, $priorities) {
if (!isset($a[$offset])) {
$a[$offset] = null;
}
if (!isset($b[$offset])) {
$b[$offset] = null;
}
if ($a[$offset] == $b[$offset]) {
return 0;
}
$aPriority = isset($priorities[$a[$offset]])
? $priorities[$a[$offset]]
: null;
$bPriority = isset($priorities[$b[$offset]])
? $priorities[$b[$offset]]
: null;
if ($aPriority == $bPriority)
return 0;
return $aPriority > $bPriority ? -1 : 1;
});
否则,您只需假定 $aPriority 小于 $bPriority(如果它不大于但可以相等)。
解决方案 2
另一种无需额外 if 情况的方法是将优先级设置为 0
而不是 null
如果未设置:
$aPriority = isset($priorities[$a[$offset]])
? $priorities[$a[$offset]]
: 0;
$bPriority = isset($priorities[$b[$offset]])
? $priorities[$b[$offset]]
: 0;
然后return减法:
return $bPriority - $aPriority;
所以它看起来像这样:
uasort($array, function ($a, $b) use ($offset, $priorities) {
if (!isset($a[$offset])) {
$a[$offset] = null;
}
if (!isset($b[$offset])) {
$b[$offset] = null;
}
if ($a[$offset] == $b[$offset]) {
return 0;
}
$aPriority = isset($priorities[$a[$offset]])
? $priorities[$a[$offset]]
: 0;
$bPriority = isset($priorities[$b[$offset]])
? $priorities[$b[$offset]]
: 0;
return $bPriority - $aPriority;
});
<?php
// an array to sort
$array = [
['type' => 'A'],
['type' => 'A'],
['type' => 'B'],
['type' => 'B'],
['type' => 'C'],
['type' => 'C'],
['type' => 'D'],
['type' => 'D'],
['type' => 'E'],
['type' => 'E'],
['type' => 'F'],
['type' => 'F'],
['type' => 'G'],
['type' => 'G'],
['type' => 'H'],
['type' => 'H'],
['type' => 'Foo'],
['type' => 'Foo'],
['type' => 'Bar'],
['type' => 'Bar'],
[0 => 'no type should be last'],
[0 => 'no type should be last'],
];
// shuffle the array
shuffle($array);
// set priorities
$priorities = [
'A' => 8,
'B' => 7,
'C' => 6,
'D' => 5,
'E' => 4,
'F' => 3,
'G' => 2,
'H' => 1,
];
uasort($array,function($a,$b) use ($priorities){
if(!isset($a['type'])){
if(!isset($b['type'])) return -1;
return 1;
}else if(!isset($b['type'])){
return -1;
}
if(isset($priorities[$a['type']])){
if(!isset($priorities[$b['type']])) return -1;
if($priorities[$a['type']] > $priorities[$b['type']]) return -1;
else if($priorities[$a['type']] < $priorities[$b['type']]) return 1;
}else if(isset($priorities[$b['type']])){
return 1;
}
return 0;
});
echo "<pre>";
print_r($array);
输出
Array
(
[21] => Array
(
[type] => A
)
[8] => Array
(
[type] => A
)
[18] => Array
(
[type] => B
)
[20] => Array
(
[type] => B
)
[6] => Array
(
[type] => C
)
[16] => Array
(
[type] => C
)
[11] => Array
(
[type] => D
)
[1] => Array
(
[type] => D
)
[7] => Array
(
[type] => E
)
[17] => Array
(
[type] => E
)
[13] => Array
(
[type] => F
)
[5] => Array
(
[type] => F
)
[15] => Array
(
[type] => G
)
[9] => Array
(
[type] => G
)
[4] => Array
(
[type] => H
)
[0] => Array
(
[type] => H
)
[19] => Array
(
[type] => Bar
)
[10] => Array
(
[type] => Foo
)
[14] => Array
(
[type] => Bar
)
[12] => Array
(
[type] => Foo
)
[2] => Array
(
[0] => no type should be last
)
[3] => Array
(
[0] => no type should be last
)
)
我决定使用@vivek_23(已编辑|修复)解决方案。
我删除了不必要的 else
并将 return 值更改为零,如果 f.e。两个偏移量都未设置。
如果 a 和 b 缺少偏移量或没有优先级,则函数应该 return 零 imo。
工作代码:
uasort($array, function ($a, $b) use ($offset, $priorities) {
if (!isset($a[$offset])) {
return !isset($b[$offset])
? 0
: 1; // down
} elseif (!isset($b[$offset])) {
return -1; // up
}
if (isset($priorities[$a[$offset]])) {
if (!isset($priorities[$b[$offset]])) {
return -1; // up
}
return $priorities[$a[$offset]] > $priorities[$b[$offset]]
? -1 // up
: 1; // down
}
return isset($priorities[$b[$offset]])
? 1 // down
: 0;
});
这允许我使用负优先级、浮动优先级并且我不覆盖优先级(参见 -99999999 @Eddie 解决方案)。
已测试
// expect A, A, B, B, C, C, ... "no type ..." at bottom
$priorities = [
'A' => 8,
'B' => 7,
'C' => 6,
'D' => 5,
'E' => 4,
'F' => 3,
'G' => 2,
'H' => 1,
];
// expect "no type ..." at bottom, ..., C, C, B, B, A, A
$priorities = [
'A' => -8,
'B' => -7,
'C' => -6,
'D' => -5,
'E' => -4,
'F' => -3,
'G' => -2,
'H' => -1,
];
// expect B, B, A, A, C, C, ... "no type ..." at bottom
$priorities = [
'A' => 6.5,
'B' => 7,
'C' => 6,
'D' => 5,
'E' => 4,
'F' => 3,
'G' => 2,
'H' => 1,
];
// expect "no type ..." at bottom, ..., C, C, A, A, B, B
$priorities = [
'A' => -6.5,
'B' => -7,
'C' => -6,
'D' => -5,
'E' => -4,
'F' => -3,
'G' => -2,
'H' => -1,
];
感谢@vivek_23
:)
您的排序有 3 个逻辑规则。宇宙飞船运算符提供了非常清晰简洁的语法,没有混乱的条件块。
只要在三向比较运算符(<=>
)两边写上相应的元素,规则就会从左到右得到遵守。当 $b
是 first/top/left 数组时,则应用 DESC 顺序;反之,first/top/left数组中的$a
等同于ASC排序。
- 按目标列 DESC 的存在进行排序
- 然后打破#1 关系,按优先级值 DESC
排序
- 然后打破#2 关系,按目标列值 ASC 排序
代码:(Demo)
function arraySortPriority(array &$array, $column, array $priorities):void
{
usort($array, function($a, $b) use ($column, $priorities) {
$aFocus = $a[$column] ?? null;
$bFocus = $b[$column] ?? null;
return [array_key_exists($column, $b), $priorities[$bFocus] ?? 0, $aFocus]
<=>
[array_key_exists($column, $a), $priorities[$aFocus] ?? 0, $bFocus];
});
}
arraySortPriority($array, 'type', $priorities);
var_export($array)
如果您的 $array
永远不会包含 null
值,则可以进一步优化此代码段以在 usort()
范围内不包含任何函数调用。
我需要一种按优先级对项目数组进行排序的方法。
这是我目前所做的工作:
function arraySortPriority(array &$array, $offset, array $priorities)
{
uasort($array, function ($a, $b) use ($offset, $priorities) {
if (!isset($a[$offset])) {
$a[$offset] = null;
}
if (!isset($b[$offset])) {
$b[$offset] = null;
}
if ($a[$offset] == $b[$offset]) {
return 0;
}
$aPriority = isset($priorities[$a[$offset]])
? $priorities[$a[$offset]]
: null;
$bPriority = isset($priorities[$b[$offset]])
? $priorities[$b[$offset]]
: null;
return $aPriority > $bPriority ? -1 : 1;
});
}
// an array to sort
$array = [
['type' => 'A'],
['type' => 'A'],
['type' => 'B'],
['type' => 'B'],
['type' => 'C'],
['type' => 'C'],
['type' => 'D'],
['type' => 'D'],
['type' => 'E'],
['type' => 'E'],
['type' => 'F'],
['type' => 'F'],
['type' => 'G'],
['type' => 'G'],
['type' => 'H'],
['type' => 'H'],
['type' => 'Foo'],
['type' => 'Foo'],
['type' => 'Bar'],
['type' => 'Bar'],
[0 => 'no type should be last'],
[0 => 'no type should be last'],
];
// shuffle the array
shuffle($array);
// set priorities
$priorities = [
'A' => 8,
'B' => 7,
'C' => 6,
'D' => 5,
'E' => 4,
'F' => 3,
'G' => 2,
'H' => 1,
];
// call
arraySortPriority($array, 'type', $priorities);
// test output
foreach ($array as $item) {
if (isset($item['type'])) {
echo "{$item['type']}\r\n";
} else {
$values = array_values($item);
echo reset($values) . PHP_EOL;
}
}
预期:
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
Foo
Foo
Bar
Bar
no type should be last
no type should be last
实际:
A
A
B
B
C
C
D
D
E
E
F
F
G
G
H
H
no type should be last <-- should be at bottom
no type should be last <-- should be at bottom
Bar
Bar
Foo
Foo
问题是没有给予 $offset
的项目应该总是排在底部。
这意味着 no type should be last
的排序应始终低于 Foo
或 Bar
。
我该怎么做?
您没有处理两个优先级相同的情况。
解决方案 1
只需将以下内容添加到您的函数中的三元前:
if ($aPriority == $bPriority)
return 0;
所以它应该是这样的:
uasort($array, function ($a, $b) use ($offset, $priorities) {
if (!isset($a[$offset])) {
$a[$offset] = null;
}
if (!isset($b[$offset])) {
$b[$offset] = null;
}
if ($a[$offset] == $b[$offset]) {
return 0;
}
$aPriority = isset($priorities[$a[$offset]])
? $priorities[$a[$offset]]
: null;
$bPriority = isset($priorities[$b[$offset]])
? $priorities[$b[$offset]]
: null;
if ($aPriority == $bPriority)
return 0;
return $aPriority > $bPriority ? -1 : 1;
});
否则,您只需假定 $aPriority 小于 $bPriority(如果它不大于但可以相等)。
解决方案 2
另一种无需额外 if 情况的方法是将优先级设置为 0
而不是 null
如果未设置:
$aPriority = isset($priorities[$a[$offset]])
? $priorities[$a[$offset]]
: 0;
$bPriority = isset($priorities[$b[$offset]])
? $priorities[$b[$offset]]
: 0;
然后return减法:
return $bPriority - $aPriority;
所以它看起来像这样:
uasort($array, function ($a, $b) use ($offset, $priorities) {
if (!isset($a[$offset])) {
$a[$offset] = null;
}
if (!isset($b[$offset])) {
$b[$offset] = null;
}
if ($a[$offset] == $b[$offset]) {
return 0;
}
$aPriority = isset($priorities[$a[$offset]])
? $priorities[$a[$offset]]
: 0;
$bPriority = isset($priorities[$b[$offset]])
? $priorities[$b[$offset]]
: 0;
return $bPriority - $aPriority;
});
<?php
// an array to sort
$array = [
['type' => 'A'],
['type' => 'A'],
['type' => 'B'],
['type' => 'B'],
['type' => 'C'],
['type' => 'C'],
['type' => 'D'],
['type' => 'D'],
['type' => 'E'],
['type' => 'E'],
['type' => 'F'],
['type' => 'F'],
['type' => 'G'],
['type' => 'G'],
['type' => 'H'],
['type' => 'H'],
['type' => 'Foo'],
['type' => 'Foo'],
['type' => 'Bar'],
['type' => 'Bar'],
[0 => 'no type should be last'],
[0 => 'no type should be last'],
];
// shuffle the array
shuffle($array);
// set priorities
$priorities = [
'A' => 8,
'B' => 7,
'C' => 6,
'D' => 5,
'E' => 4,
'F' => 3,
'G' => 2,
'H' => 1,
];
uasort($array,function($a,$b) use ($priorities){
if(!isset($a['type'])){
if(!isset($b['type'])) return -1;
return 1;
}else if(!isset($b['type'])){
return -1;
}
if(isset($priorities[$a['type']])){
if(!isset($priorities[$b['type']])) return -1;
if($priorities[$a['type']] > $priorities[$b['type']]) return -1;
else if($priorities[$a['type']] < $priorities[$b['type']]) return 1;
}else if(isset($priorities[$b['type']])){
return 1;
}
return 0;
});
echo "<pre>";
print_r($array);
输出
Array
(
[21] => Array
(
[type] => A
)
[8] => Array
(
[type] => A
)
[18] => Array
(
[type] => B
)
[20] => Array
(
[type] => B
)
[6] => Array
(
[type] => C
)
[16] => Array
(
[type] => C
)
[11] => Array
(
[type] => D
)
[1] => Array
(
[type] => D
)
[7] => Array
(
[type] => E
)
[17] => Array
(
[type] => E
)
[13] => Array
(
[type] => F
)
[5] => Array
(
[type] => F
)
[15] => Array
(
[type] => G
)
[9] => Array
(
[type] => G
)
[4] => Array
(
[type] => H
)
[0] => Array
(
[type] => H
)
[19] => Array
(
[type] => Bar
)
[10] => Array
(
[type] => Foo
)
[14] => Array
(
[type] => Bar
)
[12] => Array
(
[type] => Foo
)
[2] => Array
(
[0] => no type should be last
)
[3] => Array
(
[0] => no type should be last
)
)
我决定使用@vivek_23(已编辑|修复)解决方案。
我删除了不必要的 else
并将 return 值更改为零,如果 f.e。两个偏移量都未设置。
如果 a 和 b 缺少偏移量或没有优先级,则函数应该 return 零 imo。
工作代码:
uasort($array, function ($a, $b) use ($offset, $priorities) {
if (!isset($a[$offset])) {
return !isset($b[$offset])
? 0
: 1; // down
} elseif (!isset($b[$offset])) {
return -1; // up
}
if (isset($priorities[$a[$offset]])) {
if (!isset($priorities[$b[$offset]])) {
return -1; // up
}
return $priorities[$a[$offset]] > $priorities[$b[$offset]]
? -1 // up
: 1; // down
}
return isset($priorities[$b[$offset]])
? 1 // down
: 0;
});
这允许我使用负优先级、浮动优先级并且我不覆盖优先级(参见 -99999999 @Eddie 解决方案)。
已测试
// expect A, A, B, B, C, C, ... "no type ..." at bottom
$priorities = [
'A' => 8,
'B' => 7,
'C' => 6,
'D' => 5,
'E' => 4,
'F' => 3,
'G' => 2,
'H' => 1,
];
// expect "no type ..." at bottom, ..., C, C, B, B, A, A
$priorities = [
'A' => -8,
'B' => -7,
'C' => -6,
'D' => -5,
'E' => -4,
'F' => -3,
'G' => -2,
'H' => -1,
];
// expect B, B, A, A, C, C, ... "no type ..." at bottom
$priorities = [
'A' => 6.5,
'B' => 7,
'C' => 6,
'D' => 5,
'E' => 4,
'F' => 3,
'G' => 2,
'H' => 1,
];
// expect "no type ..." at bottom, ..., C, C, A, A, B, B
$priorities = [
'A' => -6.5,
'B' => -7,
'C' => -6,
'D' => -5,
'E' => -4,
'F' => -3,
'G' => -2,
'H' => -1,
];
感谢@vivek_23 :)
您的排序有 3 个逻辑规则。宇宙飞船运算符提供了非常清晰简洁的语法,没有混乱的条件块。
只要在三向比较运算符(<=>
)两边写上相应的元素,规则就会从左到右得到遵守。当 $b
是 first/top/left 数组时,则应用 DESC 顺序;反之,first/top/left数组中的$a
等同于ASC排序。
- 按目标列 DESC 的存在进行排序
- 然后打破#1 关系,按优先级值 DESC 排序
- 然后打破#2 关系,按目标列值 ASC 排序
代码:(Demo)
function arraySortPriority(array &$array, $column, array $priorities):void
{
usort($array, function($a, $b) use ($column, $priorities) {
$aFocus = $a[$column] ?? null;
$bFocus = $b[$column] ?? null;
return [array_key_exists($column, $b), $priorities[$bFocus] ?? 0, $aFocus]
<=>
[array_key_exists($column, $a), $priorities[$aFocus] ?? 0, $bFocus];
});
}
arraySortPriority($array, 'type', $priorities);
var_export($array)
如果您的 $array
永远不会包含 null
值,则可以进一步优化此代码段以在 usort()
范围内不包含任何函数调用。