使用月份作为键名对数组进行排序,忽略其他键名

3
Array
(
    [strMachineGroupID] => MC000027
    [strMachineGroup] => 1
    [April-201904_QTY] => 1
    [February-201902_QTY] => 1
    [January-201901_QTY] => 1
    [July-201907_QTY] => 1
    [June-201906_QTY] => 1
    [March-201903_QTY] => 1
    [May-201905_QTY] => 1
    [strMachineGroupIDUE] => MC000027
    [April-201904_UE] => 1.00
    [February-201902_UE] => 1.00
    [January-201901_UE] => 1.00
    [July-201907_UE] => 1.00
    [June-201906_UE] => 1.00
    [March-201903_UE] => 1.00
    [May-201905_UE] => 1.00
)

这是我的查询结果中的一个数组。我希望对该数组进行排序,以便结果按照月份顺序显示(从一月到十二月)。

我希望一月份和其他月份都能相邻显示。

我尝试使用:

    //     $value = (array)$value;
    //     print_r($value);
    //     print_r(asort($value));
    //     print_r(krsort($value));
    //     print_r(ksort($value)); 

但是它们确实起到了作用。我还在继续努力。

任何能够做到这一点的想法都会真正有所帮助。

5个回答

3
你需要使用uksort函数来比较键。这个回调函数将把日期键排序到开头(依赖于这些键中有一个6位数字的YYYYMM字符串),然后在日期之间进行排序。非日期键按字母顺序排序。
uksort($array, function ($k1, $k2) {
    if (preg_match('/^\w+-(\d{6})_\w+$/', $k1, $m1)) {
        if (preg_match('/^\w+-(\d{6})_\w+$/', $k2, $m2)) {
            // both have dates, sort on that
            return strcmp($m1[1], $m2[1]);
        }
        else {
            // dates sort first
            return -1;
        }
    }
    elseif (preg_match('/^\w+-(\d{6})_\w+$/', $k2, $m2)) {
        // dates sort first
        return 1;
    }
    else {
        // neither is a date, sort alphabetically
        return strcmp($k1, $k2);
    }
});

输出:

Array (
    [January-201901_UE] => 1
    [January-201901_QTY] => 1
    [February-201902_QTY] => 1
    [February-201902_UE] => 1
    [March-201903_UE] => 1
    [March-201903_QTY] => 1
    [April-201904_UE] => 1
    [April-201904_QTY] => 1
    [May-201905_UE] => 1
    [May-201905_QTY] => 1
    [June-201906_QTY] => 1
    [June-201906_UE] => 1
    [July-201907_UE] => 1
    [July-201907_QTY] => 1
    [strMachineGroup] => 1
    [strMachineGroupID] => MC000027
    [strMachineGroupIDUE] => MC000027 
)

Demo on 3v4l.org


@guradio 不用担心。如果这不完全符合您的需求,您可以使用演示来微调排序。 - Nick
不错的答案。但我建议直接在正则表达式中使用月份名称,以避免错误。 - Rulisp
@Rulisp,这就是为什么我提供了演示的原因,这样OP就可以调整它,以防正则表达式不能完全解决他的需求。如果没有看到所有数据,修改正则表达式可能会使事情变得不必要地复杂化。 - Nick

1
如果我理解正确,您需要使用uksort()函数。它允许您编写自己的比较器。但是对于没有月份索引和%monthname%之后的日期呢?

我只需要按月份排序索引。 - guradio
@guradio 所以,strMachineGroupId 应该保留其位置吗? - Rulisp
保留或移动都无所谓,我只需要月份相邻。 - guradio
1
感谢您的想法。 - guradio

1
下面的方法是通过尝试在破折号上拆分键来工作的。然后进行第二部分(索引1)的比较,如果不可用,则使用键进行比较。
例如,键July-201906_QTYJune-201906_QTY之间的比较会在uksort中被分解为201906_QTY201906_QTY之间的比较。
数字将按顺序浮动到顶部。 grunt工作是通过太空船运算符完成的。
<?php
$data =
[
    'strMachineGroupID' => MC000027,
    'strMachineGroup' => 1,
    'April-201904_QTY' => 1,
    'February-201902_QTY' => 1,
    'January-201901_QTY' => 1,
    'July-201907_QTY' => 1,
    'June-201906_QTY' => 1,
    'March-201903_QTY' => 1,
    'May-201905_QTY' => 1,
    'strMachineGroupIDUE' => MC000027,
    'April-201904_UE' => 1.00,
    'February-201902_UE' => 1.00,
    'January-201901_UE' => 1.00,
    'July-201907_UE' => 1.00,
    'June-201906_UE' => 1.00,
    'March-201903_UE' => 1.00,
    'May-201905_UE' => 1.00
];

uksort($data, function($a, $b) {
    $parts_a = explode('-', $a);
    $parts_b = explode('-', $b);

    return ($parts_a[1] ?? $a) <=> ($parts_b[1] ?? $b);
});

var_export($data);

输出:

array (
'January-201901_QTY' => 1,
'January-201901_UE' => 1.0,
'February-201902_QTY' => 1,
'February-201902_UE' => 1.0,
'March-201903_QTY' => 1,
'March-201903_UE' => 1.0,
'April-201904_QTY' => 1,
'April-201904_UE' => 1.0,
'May-201905_QTY' => 1,
'May-201905_UE' => 1.0,
'June-201906_QTY' => 1,
'June-201906_UE' => 1.0,
'July-201907_QTY' => 1,
'July-201907_UE' => 1.0,
'strMachineGroup' => 1,
'strMachineGroupID' => 'MC000027',
'strMachineGroupIDUE' => 'MC000027',
)

我可以用像“ze-2010Esd”这样的密钥来破解它,但是这个解决方案究竟是如何工作的呢? - ggorlen
@ggorlen,我添加了一个简短的解释。这个解决方案在你的示例数据上运行良好。我相信稍微调整一下就可以实现你想要的效果。如果你遇到问题,可以使用这种方法发布一个带有更多样本数据的新问题。 - Progrock
啊,我现在明白了。由于某种原因,我错过了[1]而不是[0]。不,我只是在打太极,这可能不适用于OP的使用情况。非常简洁的解决方案。 - ggorlen
@ggorlen 对不起,我把你和 OP 混淆了。我认为这是一个非常好的干净的解决方案。而且比 Nick 的更好,因为月份对更加一致(尽管 OP 没有任何具体要求)。 - Progrock
我同意,但尼克先发了帖子并获得了一些赞成的动力。个人而言,我更倾向于先考虑正确性,但由于此处要求相当模糊,所以这肯定是清洁度比赛的胜利者。谢谢分享。 - ggorlen

1
这里有另一种选项,它不依赖于字符串开头的任何内容,只需跟随连字符和字面月份名称。
uksort($arr, function($a, $b) {
    $months = array_flip(
        ["January", "February", "March", "April", "May", "June",  "July",
         "August", "September", "October", "November", "December"]
    );

    if (preg_match("`^([A-Z][a-z]+)-`", $a, $x) && array_key_exists($x[1], $months)) {
        if (preg_match("`^([A-Z][a-z]+)-`", $b, $y) && array_key_exists($y[1], $months)) {
            if ($months[$x[1]] === $months[$y[1]]) {
                return $a > $b;
            }

            return $months[$x[1]] - $months[$y[1]];
        }

        return -1;
    }

    return $a > $b;
});

输出:

Array
(
    [January-201901_UE] => 1
    [January-201901_QTY] => 1
    [February-201902_QTY] => 1
    [February-201902_UE] => 1
    [March-201903_UE] => 1
    [March-201903_QTY] => 1
    [April-201904_UE] => 1
    [April-201904_QTY] => 1
    [May-201905_UE] => 1
    [May-201905_QTY] => 1
    [June-201906_QTY] => 1
    [June-201906_UE] => 1
    [July-201907_UE] => 1
    [July-201907_QTY] => 1
    [strMachineGroup] => 1
    [strMachineGroupID] => MC000027
    [strMachineGroupIDUE] => MC000027
)

试一下!


0

asort函数将按值对数组进行排序。ksort函数将按键(使用字符串排序)对数组进行排序。如果您的键只包含数字,则可以使用ksort函数对其进行排序。但是,对于字符串键strMachineGroup、strMachineGroupID和strMachineGroupIDUE,您必须进行特殊处理。

Array
(
    [strMachineGroupID] => MC000027
    [strMachineGroup] => 1
    [201904_QTY] => 1
    [201902_QTY] => 1
    [201901_QTY] => 1
    [201907_QTY] => 1
    [201906_QTY] => 1
    [201903_QTY] => 1
    [201905_QTY] => 1
    [strMachineGroupIDUE] => MC000027
    [201904_UE] => 1.00
    [201902_UE] => 1.00
    [201901_UE] => 1.00
    [201907_UE] => 1.00
    [201906_UE] => 1.00
    [201903_UE] => 1.00
    [201905_UE] => 1.00
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接