繁体   English   中英

对可能包含日期和缩写月份名称的字符串数组进行排序

[英]Sort array of strings which may containing day and abbreviated month names

我想按升序对数组进行排序,但正常的排序方法不会尊重可选出现的日期和月份子字符串。 包含“反馈”的字符串必须按月和日排序,因为它们出现在日历上。

甚至natsort()都不会按照我的要求尊重月份。

示例数组:

$array = [
    "Feedback 13 okt",
    "Feedback 11 okt",
    "Feedback 12 okt",
    "Sweet",
    "Feedback 9 okt",
    "Feedback 6 okt",
    "Feedback 8 jun",
    "Fixes",
    "Realisation",
    "Feedback 22 mar",
    "Do something",
    "Feedback 3 maj",
    "Feedback 1 dec",
];

期望的结果:

[
    'Do something',
    'Feedback 22 mar',
    'Feedback 3 maj',
    'Feedback 8 jun',
    'Feedback 6 okt',
    'Feedback 9 okt',
    'Feedback 11 okt',
    'Feedback 12 okt',
    'Feedback 13 okt',
    'Feedback 1 dec',
    'Fixes',
    'Realisation',
    'Sweet',
]

您应该使用usort()来实现自己的比较函数,该函数首先比较两个没有任何数字的字符串(为此使用preg_replace('/ \\ d + /','',$ str) ),然后,如果将两个字符串比较为平等,使用strnatcmp()来比较的字符串(包括数字) natsort()方法。

usort($array, function($a, $b) {
    $cmp = strcmp(preg_replace('/\d+/', '', $a), preg_replace('/\d+/', '', $b));
    if ($cmp) {
        return $cmp;
    } else {
        return strnatcmp($a, $b);
    }
});

奇怪的是,我对所需排序逻辑的解释似乎比提问者的更深入。

我认为规则如下:

  1. 按第一个单词排序,然后如果平局,
  2. 如果是“Feedback...”字符串,则解析字符串并按月排序,然后按天排序,如果仍然是平局,
  3. “自然地”对整个字符串进行排序,并且不区分大小写。

代码:(演示

$array = [
    "Feedback 13 okt",
    "Feedback 11 okt",
    "Feedback 12 okt",
    "Sweet",
    "Feedback 9 okt",
    "Feedback 6 okt",
    "Feedback 8 jun",
    "Fixes",
    "Realisation",
    "Feedback 22 mar",
    "Do something",
    "Feedback 3 maj",
    "Feedback 1 dec",
];

$mmm = array_flip([
    "jan", "feb", "mar", "apr",
    "maj", "jun", "jul", "aug",
    "sep", "okt", "nov", "dec"
]);

usort(
    $array,
    fn($a, $b) =>
        strtok($a, ' ') <=> strtok($b, ' ')

        ?: (sscanf($a, "Feedback %d %s", $aDay, $aMon)
            ? [$mmm[$aMon] ?? 13, $aDay]
            : [13, 32])
           <=>
           (sscanf($b, "Feedback %d %s", $bDay, $bMon)
            ? [$mmm[$bMon] ?? 13, $bDay]
            : [13, 32])

        ?: natcasecmp($a, $b)
);
var_export($array);

输出:

array (
  0 => 'Do something',
  1 => 'Feedback 22 mar',
  2 => 'Feedback 3 maj',
  3 => 'Feedback 8 jun',
  4 => 'Feedback 6 okt',
  5 => 'Feedback 9 okt',
  6 => 'Feedback 11 okt',
  7 => 'Feedback 12 okt',
  8 => 'Feedback 13 okt',
  9 => 'Feedback 1 dec',
  10 => 'Fixes',
  11 => 'Realisation',
  12 => 'Sweet',
)

通过使用“Elvis 运算符”( ?: ),后续的决胜局进程仅在必要时执行。 这是最佳实践并提供最佳性能。

您可以从日期部分创建DateTime并在usort中按时间顺序对日期条目进行排序。 我无法让我的区域设置正确地使用您的语言,因此我不得不编辑数据以使用英语月份缩写。 我假设您已为您的语言环境正确配置了 PHP,因此这应该可以在您的系统上使用您的数据。

usort($array, function($a, $b) { 
  $pattern = '/^\w*\s+/'; // ("Feedback ") matches first word + whitespaces
  $fmt = 'j M';
  $a_date = DateTime::createFromFormat($fmt, preg_replace($pattern, '', $a));
  $b_date = DateTime::createFromFormat($fmt, preg_replace($pattern, '', $b));
  if($a_date && $b_date) // both are dates
    return $a_date <=> $b_date;
  // fallback to compare as strings
  return strcmp($a, $b);
});

因此,使用以下$array值:

$array = [
    "Feedback 13 oct",
    "Feedback 11 oct",
    "Feedback 12 oct",
    "Sweet",
    "Feedback 9 oct",
    "Feedback 6 oct",
    "Feedback 8 jun",
    "Fixes",
    "Realisation",
    "Feedback 22 mar",
    "Do something",
    "Feedback 3 may",
    "Feedback 1 dec",
];

调用上面的usort function var_export($array); 将output如下:

array (
  0 => 'Do something',
  1 => 'Feedback 22 mar',
  2 => 'Feedback 3 may',
  3 => 'Feedback 8 jun',
  4 => 'Feedback 6 oct',
  5 => 'Feedback 9 oct',
  6 => 'Feedback 11 oct',
  7 => 'Feedback 12 oct',
  8 => 'Feedback 13 oct',
  9 => 'Feedback 1 dec',
  10 => 'Fixes',
  11 => 'Realisation',
  12 => 'Sweet',
)

看到它在这里工作: https://onlinephp.io/c/dc5d6

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM