如何实现基于连字符分隔的子串的多个自定义排序规则?

问题描述 投票:0回答:3

我有一个数组的文件名是这样的形式。

"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 来实现这个功能吗?

php arrays regex sorting usort
3个回答
5
投票

有一个排序函数,它以比较法作为参数。你可以像这样使用它。

$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");

1
投票

不如这样:

$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
)

-1
投票

在把你的字符串分成有意义的部分后,我觉得一个层叠的三元表达式集要比 if 块来达到后续的平局条件。

另外,使用 version_compare() 对于您的中间子串来说是非常合适的 -- 这将确保当您的majorminormicro版本进入两位数领域时,自然排序仍然有效。.

将你的自定义优先级数组传递到自定义函数分值中,并使用 use() 宣言。

编码。(演示)

$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',
)

或者,你也可以在平衡数组上使用单个飞船运算符比较来达到完全相同的效果: (演示)

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];
});

我相信第一个片段的好处是,除非达成,否则后续的平局不会被执行。 第二个片段将是填充所有元素,无论是否需要比较。 如果不正确,欢迎大家用评论来纠正我。

© www.soinside.com 2019 - 2024. All rights reserved.