【问题标题】:PHP - More efficient method for sorting alphanumeric keysPHP - 排序字母数字键的更有效方法
【发布时间】:2014-09-15 07:22:17
【问题描述】:

注意:我编辑了示例并将所有值替换为 1。值无关紧要,只有键才重要。我之前写的值导致了一个误解。对不起。

注意:a/b 块始终是连续的。我添加这个是因为不清楚。如果有 3a/3b 和 5a/5b,总会有 4a/4b 而不仅仅是 4。

我的数组包含带有前导零的编号键。有时,这些编号的键有两种变体,我通过使用后缀 a 和 b 来区分它们。有或没有变化的键的数量是未知的,但是永远不会超过 2 位;即最高数字键是'09'。

问题是这些数组键需要按数字排序,但是当存在后缀时,它们应该优先。单独使用 ksort() 并不能实现这一点。

例如,ksort() 给了我这个:

$arr = array(
    '01' => 1,
    '02' => 1,
    '03a' => 1,
    '03b' => 1,
    '04a' => 1,
    '04b' => 1,
    '05a' => 1,
    '05b' => 1,
    '06' => 1,
);

但是,我需要这个:

$arr = array(
    '01' => 1,
    '02' => 1,
    '03a' => 1,
    '04a' => 1,
    '05a' => 1,
    '03b' => 1,
    '04b' => 1,
    '05b' => 1,
    '06' => 1,
);

我使用一些花哨的编码体操来获得我想要的东西,但这并不漂亮。我想知道是否有更好、更清洁的方法?


这就是我的工作。

1) 我使用 ksort(),它给了我上面两个数组中的第一个。 (那个还不是我想要的。)

2) 我创建了两个数组。一个用于“a”后缀,另一个用于“b”后缀。

$arr_a = array();
$arr_b = array();

foreach ($arr as $k => $v) {
    if (substr($k, 2) == 'a') {
        $arr_a[$k] = $v;
    } else if (substr($k, 2) == 'b') {
        $arr_b[$k] = $v;
    }
}

3)我合并两个后缀数组。

$arr_suffixes = array_merge($arr_a, $arr_b);

4) 我对原始数组进行切片,以便得到后缀之前的部分和后缀之后的部分。

$i = array_search(key($arr_suffixes), array_keys($arr));
$length = count($arr_suffixes);

$arr_before_suffixes = array_slice($arr, 0, $i);
$arr_after_suffixes = array_slice($arr, $i + $length);

5) 使用 array_merge,我重新组合切片数组以创建我需要的数组。

$arr = array_merge($arr_before_suffixes, $arr_suffixes);
$arr = array_merge($arr, $arr_after_suffixes);

最后,我们得到了正确的 $arr。难道没有更好的方法来做到这一点吗?感觉真的很丑。

【问题讨论】:

  • 这不会发生。你要么拥有'04'键,要么拥有'04a'和'04b'。注意:这些值无关紧要。他们在下面抛出了一些答案。只有键很重要。
  • 我不明白。 02
  • 想象一下,用数字排序,但如果数字也有与字母 ab 比这样的排序 0a4,0a5,0b4,0b5
  • @GeorgeGarchagudashvili ,但空格多于或少于字母 a 或 b?
  • @derp - a/b 块总是连续的。我应该提到这一点。所以,如果 3a/3b 和 5a/5b 总是会有 4a/4b 而不仅仅是 4。

标签: php arrays sorting


【解决方案1】:

你没有正式的规则。我来猜一猜。

$arr = array(
    '01' => 1,
    '02' => 1,
    '03a' => 1,
    '03b' => 1,
    '04a' => 1,
    '04b' => 1,
    '05a' => 1,
    '05b' => 1,
    '06' => 1,
);

uksort($arr, function($item1, $item2)
    {
    $last1 = substr($item1, -1);
    $last2 = substr($item2, -1);
    // one of the items is a number or last letters matches
    if (is_numeric($last1) || is_numeric($last2) || $last1 == $last2) 
        // simple number comparison
        return $item1 - $item2;
    else
        // natural order comparison
        return $last1 > $last2 ? 1 : -1;
    });

var_dump($arr);

【讨论】:

  • 我没有正式的规则?是的,我在上面详细解释!谢谢,你的例子有效。这很有帮助。
  • 是的!我只是想将您的 usort 转换为 uksort。这太棒了。谢谢!
【解决方案2】:

natsort() 功能将帮助您:

$arr = array(
    '01' => 1,
    '02' => 1,
    '03a' => 1,
    '03b' => 1,
    '04a' => 1,
    '04b' => 1,
    '05a' => 1,
    '05b' => 1,
    '06' => 1,
);
$keys = array_keys($arr);
natsort($keys);
$result = array();
foreach ($keys as $key) {
    $result[$key] = $arr[$key];
}
print_r($result); // Your expected result

【讨论】:

  • 这是错误的。由于实际值,而不是键,您会通过“意外”获得预期结果。
  • 是的,我不应该给出这样的值。也许我应该改变我的例子?值可以是任何值。
  • @derp - 已编辑。这教会了我如何做到精确。
  • @plutov.by 是的,正如derp所说,在示例中,您给出的数组根本没有改变。
  • 我有一个非常不规则的键,除此之外无法通过任何其他方式进行排序。尝试了所有其他解决方案。谢谢@plutov.by!
【解决方案3】:
$arr = array(
    '01' => 1,
    '02' => 2,
    '03a' => 3,
    '03b' => 6,
    '04a' => 4,
    '04b' => 7,
    '05a' => 5,
    '05b' => 8,
    '06' => 9,
);

uksort(
    $arr,
    function($a, $b) {
        sscanf($a, '%d%s', $an, $as);
        sscanf($b, '%d%s', $bn, $bs);
        if ($as === null || $bs === null || $as === $bs) {
            return $an - $bn;
        }
        return strcmp($as, $bs);
    }
);
var_dump($arr);

【讨论】:

  • 看起来比我的干净。
  • @sectus - 它仍然不会非常有效,必须为快速排序的每次比较进行回调;但它真的和你的并没有太大的不同......使用 sscanf 代替 substr 和 strcmp 而不是你的三元
  • 你觉得我的OP中的方法效率更高吗?
  • 感谢这个变化,很高兴看到不同的想法。真的很有帮助。
  • @Pierre - 这可能取决于您的数据量,使用 uksort 处理小数据量会非常有效....但是当处理较大的数据量时,array_multisort 可能会更好
猜你喜欢
  • 1970-01-01
  • 2017-06-05
  • 2021-05-03
  • 1970-01-01
  • 2021-09-21
  • 2017-02-26
  • 1970-01-01
  • 2021-12-08
相关资源
最近更新 更多