【问题标题】:How to implement multiple custom sorting rules based on hyphen-delimited substrings?如何实现基于连字符分隔的子字符串的多个自定义排序规则?
【发布时间】:2012-01-16 20:25:41
【问题描述】:

我有一个这种形式的文件名数组:

“A - 1.2 - 平面图.PDF”

我需要先按开头的类别对数组进行排序,顺序如下:

1. Category: A
2. Category: ESC
3. Category: C
4. Category: M
5. Category: E
6. Category: P

然后我需要按类别后面的数字对数组进行排序。

这是一个要排序的数组的例子:

$arr[0] = "A - 1.0 - Title Page.PDF";
$arr[1] = "A - 2.2 - Enlarged Floor Plans";
$arr[2] = "A - 2.1.0 - Structural Details.PDF";
$arr[3] = "E - 1.0 - Electrical Title Page.PDF";
$arr[4] = "A - 1.2 - Floor Plan.PDF";
$arr[5] = "P - 1.0 - Plumbing Title Page.PDF";
$arr[6] = "A - 2.1.1 - Structural Details.PDF";
$arr[7] = "C - 1.0 - Civil Title Page.PDF";
$arr[8] = "M - 1.0 - Mechanical Title Page.PDF";
$arr[9] = "ESC - 1.0 - Erosion Control Plan.PDF";

理想情况下,这个数组会变成

$arr[0] = "A - 1.0 - Title Page.PDF";
$arr[1] = "A - 1.2 - Floor Plan.PDF";
$arr[2] = "A - 2.1.0 - Structural Details.PDF";
$arr[3] = "A - 2.1.1 - Structural Details.PDF";
$arr[4] = "A - 2.2 - Enlarged Floor Plans";
$arr[5] = "ESC - 1.0 - Erosion Control Plan.PDF";
$arr[6] = "C - 1.0 - Civil Title Page.PDF";
$arr[7] = "M - 1.0 - Mechanical Title Page.PDF";
$arr[8] = "E - 1.0 - Electrical Title Page.PDF";
$arr[9] = "P - 1.0 - Plumbing Title Page.PDF";

我有以下正则表达式用于对文件名进行适当的分组:

^([A-Z]+?) ?- ?([0-9]+)\.([0-9]+)(\.([0-9]+))?.*$

我希望数组按第 1 组排序,然后按第 2 组,然后按第 3 组。如果第 5 组存在,则按第 5 组排序。忽略第 4 组。

按字典顺序对类别进行排序可能更容易。如果是这样,那就没问题了;尽管最好按上述顺序对它们进行排序。

有没有办法用 PHP 做到这一点?

【问题讨论】:

  • 排序后不应该是这个顺序:A - 1.0, A - 1.2, A - 2.1.0, A - 2.1.1, A - 2.2, C - 1.0, E - 1.0, ESC - 1.0, M - 1.0, P - 1.0吗?
  • @Lolo,不是吗?我将您的排序版本与我的进行了比较。我找不到区别。
  • 你有:[...],ESC,C,M,E,[...] 我只是想知道为什么不:[...] C、E、ESC、M、[...]
  • @Lolo 类别是自定义顺序。这些是施工文件。土木工程中通常包含侵蚀控制计划,因为它们通常由同一家公司创建。
  • 很公平。我对我的答案进行了更改。现在有完整的比较功能,可以按照您想要的方式对元素进行排序。

标签: php arrays regex sorting usort


【解决方案1】:

在将你的字符串分解成有意义的部分之后,我觉得一组级联的三元表达式比 if 块更整洁,以达到后续的平局条件。

此外,使用 version_compare() 非常适合您的中间子字符串 - 这将确保当您的主要/次要/微型版本进入两位数区域时,自然排序仍然有效强>。

使用use() 声明将您的自定义优先级数组传递到自定义函数得分中。

代码:(Demo)

$arr = [
    "A - 1.0 - Title Page.PDF",
    "A - 2.2 - Enlarged Floor Plans",
    "A - 2.1.0 - Structural Details.PDF",
    "E - 1.0 - Electrical Title Page.PDF",
    "A - 1.2 - Floor Plan.PDF",
    "P - 1.0 - Plumbing Title Page2.PDF",
    "A - 2.1.1 - Structural Details.PDF",
    "C - 1.0 - Civil Title Page.PDF",
    "M - 1.0 - Mechanical Title Page.PDF",
    "ESC - 1.0 - Erosion Control Plan.PDF",
    "P - 1.0 - Plumbing Title Page.PDF",
];

$priorities = array_flip(['A', 'ESC', 'C', 'M', 'E', 'P']);

usort($arr, function ($a, $b) use ($priorities) {
    [$categoryA, $versionA, $nameA] = explode(' - ', $a, 3);
    [$categoryB, $versionB, $nameB] = explode(' - ', $b, 3);

    return $priorities[$categoryA] <=> $priorities[$categoryB]  // priorities as first criteria
        ?: version_compare($versionB, $versionA)                // then descending versions as second criteria
        ?: $nameA <=> $nameB;                                   // then compare names ascending
});
var_export($arr);

输出:

array (
  0 => 'A - 2.2 - Enlarged Floor Plans',
  1 => 'A - 2.1.1 - Structural Details.PDF',
  2 => 'A - 2.1.0 - Structural Details.PDF',
  3 => 'A - 1.2 - Floor Plan.PDF',
  4 => 'A - 1.0 - Title Page.PDF',
  5 => 'ESC - 1.0 - Erosion Control Plan.PDF',
  6 => 'C - 1.0 - Civil Title Page.PDF',
  7 => 'M - 1.0 - Mechanical Title Page.PDF',
  8 => 'E - 1.0 - Electrical Title Page.PDF',
  9 => 'P - 1.0 - Plumbing Title Page.PDF',
  10 => 'P - 1.0 - Plumbing Title Page2.PDF',
)

或者,您可以在平衡数组上使用单个宇宙飞船运算符比较以获得完全相同的效果:(Demo)

usort($arr, function ($a, $b) use ($priorities) {
    [$categoryA, $versionA, $nameA] = explode(' - ', $a, 3);
    [$categoryB, $versionB, $nameB] = explode(' - ', $b, 3);

    return [$priorities[$categoryA], version_compare($versionB, $versionA), $nameA]
           <=> 
           [$priorities[$categoryB], version_compare($versionA, $versionB), $nameB];
});

我相信第一个 sn-p 的好处是除非达到,否则不会执行后续的决胜局。第二个 sn-p 将填充所有元素,无论是否需要比较。如果这不正确,欢迎任何人通过评论纠正我。

【讨论】:

    【解决方案2】:

    有以比较方法为参数的排序功能。例如,您可以像这样使用它:

    $order = array('A', 'ESC', 'C', 'M', 'E', 'P'); // order of categories
    $order = array_flip($order); // flip order array, it'll look like: ('A'=>0, 'ESC'=>1, ...)
    
    function cmp($a, $b)
    {
        global $order;
    
        $ma = array();
        $mb = array();
        preg_match('/^([A-Z]+?) ?- ?([0-9]+)\.([0-9]+)(?:\.([0-9]+))?.*$/', $a, $ma);
        preg_match('/^([A-Z]+?) ?- ?([0-9]+)\.([0-9]+)(?:\.([0-9]+))?.*$/', $b, $mb);
    
        if ($ma[1] != $mb[1]) {
            return ($order[$ma[1]] < $order[$mb[1]]) ? -1 : 1;
        }
        if ($ma[2] != $mb[2]) {
            return $ma[2] < $mb[2] ? -1 : 1;
        }
        if ($ma[3] != $mb[3]) {
            return $ma[3] < $mb[3] ? -1 : 1;
        }
        // I've changed a regex a little bit, so the last number is 4th group now
        if (@$ma[4] != @$mb[4]) { 
            return @$ma[4] < @$mb[4] ? -1 : 1;
        }
        return 0;
    }
    usort($arr, "cmp");
    

    【讨论】:

      【解决方案3】:

      怎么样:

      $arr[0] = "A - 1.0 - Title Page.PDF";
      $arr[1] = "A - 2.2 - Enlarged Floor Plans";
      $arr[2] = "A - 2.1.0 - Structural Details.PDF";
      $arr[3] = "E - 1.0 - Electrical Title Page.PDF";
      $arr[4] = "A - 1.2 - Floor Plan.PDF";
      $arr[5] = "P - 1.0 - Plumbing Title Page.PDF";
      $arr[6] = "A - 2.1.1 - Structural Details.PDF";
      $arr[7] = "C - 1.0 - Civil Title Page.PDF";
      $arr[8] = "M - 1.0 - Mechanical Title Page.PDF";
      $arr[9] = "ESC - 1.0 - Erosion Control Plan.PDF";
      
      
      function cmp($a,$b) {
          $arr_a = split(' - ', $a);
          $arr_b = split(' - ', $b);
          if ($arr_a[0] == $arr_b[0])
              return strcmp($arr_a[1], $arr_b[1]);
          return strcmp($arr_a[0], $arr_b[0]);
      }
      
      usort($arr, "cmp");
      print_r($arr);
      

      输出:

      Array
      (
          [0] => A - 1.0 - Title Page.PDF
          [1] => A - 1.2 - Floor Plan.PDF
          [2] => A - 2.1.0 - Structural Details.PDF
          [3] => A - 2.1.1 - Structural Details.PDF
          [4] => A - 2.2 - Enlarged Floor Plans
          [5] => C - 1.0 - Civil Title Page.PDF
          [6] => E - 1.0 - Electrical Title Page.PDF
          [7] => ESC - 1.0 - Erosion Control Plan.PDF
          [8] => M - 1.0 - Mechanical Title Page.PDF
          [9] => P - 1.0 - Plumbing Title Page.PDF
      )
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-28
        • 1970-01-01
        • 2012-02-27
        • 1970-01-01
        • 2013-10-02
        • 1970-01-01
        相关资源
        最近更新 更多