提问人:Uchenna Ajah 提问时间:4/21/2023 最后编辑:mickmackusaUchenna Ajah 更新时间:4/22/2023 访问量:97
按负数、大写字母、符号、小写字母和正数自定义对平面数组进行排序
Custom sort flat array by negative numbers, uppercase letters, symbols, lowercase letters, then positive numbers
问:
我一直在绞尽脑汁地试图解决这个挑战。
PHP默认函数没有提供解决方案,但使用起来也不容易。sort
usort
这就是我要解决的问题。我按以下顺序创建了一个数组:
$data = array( '_', '@', ...range(-10, 10), ...range('A', 'Z'), ...range('a', 'z') )
现在我想对这个数组进行排序,以便:usort
negative
数字是第一位的,uppercase
接下来是字母_
&@
字符如下lowercase
字母如下- 最后数字结束订单
positive
有点像:
/*
array(
"-10",
"-9",...
"A",
"B",...
"_",
"@", // @ may come first
"a",
"b",...
"1",
"2"...
) */
有什么方法可以解决这个问题吗?
我试过了什么?
usort($data, function($a,$b) {
if( is_numeric($a) && (int)$a < 0 ) return -1; // take negative number to start
else {
if( !is_numeric($a) ) {
if( is_numeric($b) && (int)$b > 0 ) return -1;
else return $b < $a ? 1 : 0;
} else return 1; // take positive number to end
}
});
答:
3赞
Sammitch
4/21/2023
#1
把 看作是一个层次结构。您有 5 个要排序的不重叠“类”:负数、大写、符号、小写、正数。因此,首先确定类排序,如果两个项目的类相同,则比较它们的值。
class MySorter {
const CLASS_NUM_NEG = 0;
const CLASS_STR_UC = 1;
const CLASS_STR_OT = 2;
const CLASS_STR_LC = 3;
const CLASS_NUM_POS = 4;
static function get_class($item) {
switch(gettype($item)) {
case 'integer':
case 'float':
return ($item < 0) ? self::CLASS_NUM_NEG : self::CLASS_NUM_POS;
case 'string':
$ord = ord($item[0]);
// note: below ord() calls are illustrative, and
// should be replaced with non-computed values to
// avoid repetitive work.
if( $ord >= ord('A') && $ord <= ord('Z')) {
return self::CLASS_STR_UC;
} else if( $ord >= ord('a') && $ord <= ord('z')) {
return self::CLASS_STR_LC;
} else {
return self::CLASS_STR_OT;
}
default:
throw new \Exception("Unhandled type: " . gettype($item));
}
}
static function compare($a, $b) {
$res = self::get_class($a) <=> self::get_class($b);
if( $res !== 0 ) { return $res; }
return $a <=> $b;
}
}
$data = [ '_', '@', ...range(-10, 10), ...range('A', 'Z'), ...range('a', 'z') ];
usort($data, ['MySorter', 'compare']);
echo json_encode($data);
旁白:类可以用作包含相关函数和变量的 ersatz 命名空间,这样您就可以比完全内联或在本地/全局命名空间中转储某些内容更好地将逻辑框起来。
输出:
[-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","@","_","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",0,1,2,3,4,5,6,7,8,9,10]
0赞
mickmackusa
4/21/2023
#2
也许对于某些开发人员来说,使用正则表达式会更难阅读/维护,我没有费心比较性能,但它确实提供了一些不错的代码简洁性。在正则表达式模式中订购一系列可选捕获组。正则表达式引擎将尝试满足最早出现的子模式,其余的捕获组甚至不会在匹配数组 () 中表示。然后,由于 PHP 在比较实际数据之前会按数组的计数对数组进行排序,因此元素最少的条目将首先按 排序。当然,这可以通过 pattern 修饰符进行增强,以尊重多字节字符串。$m[]
$m
array_multisort()
u
代码:(演示)
$m = [];
foreach ($data as $v) {
preg_match('/(-\d+)?([A-Z]+)?([^A-Za-z0-9]+)?([a-z]+)?(\d+)?/', $v, $m[]);
// ^^^- positive integers
// ^^^^^^- lowercase letters
// ^^^^^^^^^^^^^- non-letters, non-numbers
// ^^^^^^- uppercase letters
// ^^^^- negative integers
}
array_multisort($m, $data);
var_export($data);
更直观且更易于扩展/维护的是将回退比较与速记三元和宇宙飞船操作员比较一起使用,直到常规排序合适。
代码:(演示)(或演示)
usort(
$data,
fn($a, $b) => ($b < 0 <=> $a < 0) // prioritize negatives
?: (ctype_upper((string) $b) <=> ctype_upper((string) $a)) // prioritize uppercase letters
?: (is_int($a) <=> is_int($b)) // deprioritize integers
?: ($a <=> $b) // sort normally
);
var_export($data);
或者,如果性能是一个问题,则通过准备计算数组来减少所需函数调用的总数,然后调用 。(演示array_multisort()
)
$negatives = [];
$uppers = [];
$integers = [];
foreach ($data as $v) {
$negatives[] = $v < 0;
$uppers[] = ctype_upper((string) $v);
$integers[] = is_int($v);
}
array_multisort(
$negatives,
SORT_DESC,
$uppers,
SORT_DESC,
$integers,
$data
);
var_export($data);
相关:
评论
$a
$b