按键排序/分组数组

4

TL;DR

如何按key对数组进行排序/分组,而不会在数组中添加另一个级别(由jQuery插件解析的数据)?

细节

我正在构建一个数组,将其返回给某些<select> DOM元素。

它以CC(发动机大小)作为参数,并将其用作关键字,问题在于之后对数组进行排序。

假设用户选择了这些CC范围:

50、100、125

50有32个选项可用

100有3个选项可用

125有12个选项可用

我的当前代码循环通过CC,执行SQL获取选项,并使用循环计数器创建以下关键字:

$options[$cc. $id] = $someValue;

这个方法可以按预期工作,但是我的输出显示的结果并不完全符合我需要的顺序( CC ASC - 所有50应该先显示在一起)。问题在于,50和32的组合生成的键值为 5031 ,100和3的组合生成的键值为 1002 ,125和12的组合生成的键值为 12511 。现在你们应该已经清楚地看到了问题。5031大于1002,所以在循环计数器传递9的情况下,50cc选项要比100cc选项更优。
(仅供参考,示例输出为):

50cc Option 1
50cc Option 2
50cc Option 3
50cc Option 4
50cc Option 5
100cc Option 1
100cc Option 2
100cc Option 3
50cc Option 6
50cc Option 7

也许最初的问题出在我如何创建键上,但我尝试使用ksort和几个不同的标志来尝试实现我的目标,但没有一个标志似乎能够针对我想要的内容。
SORT_REGULAR - compare items normally (don't change types)
SORT_NUMERIC - compare items numerically
SORT_STRING - compare items as strings
SORT_LOCALE_STRING - compare items as strings, based on the current locale. It uses the locale, which can be changed using setlocale()
SORT_NATURAL - compare items as strings using "natural ordering" like natsort()
SORT_FLAG_CASE - can be combined (bitwise OR) with SORT_STRING or SORT_NATURAL to sort strings case-insensitively

如何在不向数组添加另一层的情况下对键进行排序/分组(数据由需要特定格式的jQuery插件解析)?

编辑:完整脚本

<?php
    if (strpos(PHP_OS, 'Linux') > -1) {
        require_once $_SERVER['DOCUMENT_ROOT']. '/app/connect.php';
    } else {
        require_once getcwd(). '\\..\\..\\..\\..\\app\\connect.php';
    }

    $make = $_POST['make'];
    $cc = $_POST['cc'];

    $sql = 'SELECT * FROM `table`
                WHERE `UKM_CCM` = :cc
                AND `UKM_Make` = :make 
                ORDER BY `UKM_Model`, `UKM_StreetName`, `Year` ASC;';

    $options = array();

    foreach ($cc as $k => $value)
    {
        $res = $handler->prepare($sql);
        $res->execute(array(':cc' => $value, ':make' => $make));

        $data = $res->fetchAll(PDO::FETCH_ASSOC);

        $i = 0;

        if (count($data) > 0) {
            foreach ($data as $result)
            {
                $arrayKey = sprintf('%03d%02d', $cc, $i);

                $epid = $result['ePID'];
                $make = $result['UKM_Make'];
                $model = $result['UKM_Model'];
                $cc = $result['UKM_CCM'];
                $year = $result['Year'];
                $sub = $result['UKM_Submodel'];
                $street = $result['UKM_StreetName'];

                $options[$arrayKey]['name'] = $make. ' ' .$model. ' ' .$cc. ' ' .$year. ' ' .$sub. ' ' .$street;
                $options[$arrayKey]['value'] = $epid;
                $options[$arrayKey]['checked'] = false;

                $options[$arrayKey]['attributes']['data-epid'] = $epid;
                $options[$arrayKey]['attributes']['data-make'] = $make;
                $options[$arrayKey]['attributes']['data-model'] = $model;
                $options[$arrayKey]['attributes']['data-cc'] = $cc;
                $options[$arrayKey]['attributes']['data-year'] = $year;
                $options[$arrayKey]['attributes']['data-sub'] = $sub;
                $options[$arrayKey]['attributes']['data-street'] = $street;

                $i++;
            }
        }
    }

    ksort($options, SORT_STRING);

    echo json_encode($options);

2
你能在SQL查询中对它们进行排序吗? - Mickaël Leger
@MickaelLeger,它们是分开的SQL执行,因此没有...不过现在你提到了,也许我可以更改我的SQL以使用WHERE IN ($values) - 给我一点时间! :) - treyBake
你能拥有一个非数字键吗?例如 "50.3"? - Royal Wares
@AlexanderDeSousa 这是个好主意 - 但是 JSON 那边似乎不允许 'value.counter' - 它会返回 'value.' 作为一个键 :/ 不过想法很好 :) 我会尝试使用 _ 或其他什么符号 :) - treyBake
@ThisGuyHasTwoThumbs 没问题,我有一个使用uksort的书面解决方案,只要你的键能够有某种分隔符,那么我就可以做到。 - Royal Wares
4个回答

2

您可以将密钥格式化为cc有3位数字,选项有2位数字...

$options[sprintf('%03d%02d', $cc, $id)] = $someValue;

这应该会给你两个键:0503110002

然后使用SORT_STRING,强制它将它们作为字符串排序(尽管它们也可以作为数字排序)


理论上看起来不错,但出于某种原因,它没有提供125cc选项X,只有100cc选项、一些50cc选项和更多的125cc选项等。:S - treyBake
你能否在你的问题中添加更多代码(包括SQL),这样可以更好地理解它。 - Nigel Ren
你检查了 echo json_encode($options); 的输出吗?能否添加一下(抱歉,我得出门几个小时)? - Nigel Ren
说实话,我并没有用你的代码,而是用了我的原始代码 - 看看它会给我什么结果 :) - treyBake

0
如果您可以将关键字“additional”添加到数组中,您可以创建一个usort()函数来按照您的需求对数组进行排序:
$arrSort = [50, 100, 125];
$arrData = [
    501 => [
        'foo',
        'bar',
        'arrayKey' => 501
    ],
    504 => [
        'foo',
        'bar',
        'arrayKey' => 504
    ],
    1002 => [
        'foo',
        'bar',
        'arrayKey' => 1002
    ],
    10045 => [
        'foo',
        'bar',
        'arrayKey' => 10045
    ],
    1251 => [
        'foo',
        'bar',
        'arrayKey' => 1251
    ],
    5045 => [
        'foo',
        'bar',
        'arrayKey' => 5045
    ]
];

usort($arrData, function($a, $b) use ($arrSort)
{
    $posA = array_search(substr($a['arrayKey'], 0, 2), $arrSort);
    if ($posA === false) {
        $posA = array_search(substr($a['arrayKey'], 0, 3), $arrSort);
    }

    $posB = array_search(substr($b['arrayKey'], 0, 2), $arrSort);
    if ($posB === false) {
        $posB = array_search(substr($b['arrayKey'], 0, 3), $arrSort);
    }

    return $posA - $posB;
});

在这个示例中,该函数的返回值为:

array:6 [▼ 0 => array:3 [▼ 0 => "foo" 1 => "bar" "arrayKey" => 501 ] 1 => array:3 [▼ 0 => "foo" 1 => "bar" "arrayKey" => 504 ] 2 => array:3 [▼ 0 => "foo" 1 => "bar" "arrayKey" => 5045 ] 3 => array:3 [▼ 0 => "foo" 1 => "bar" "arrayKey" => 1002 ] 4 => array:3 [▼ 0 => "foo" 1 => "bar" "arrayKey" => 10045 ] 5 => array:3 [▼ 0 => "foo" 1 => "bar" "arrayKey" => 1251 ] ]


我尝试过了,按正确的数字顺序排序(例如100、125先显示,但50最后:S)。 - treyBake
@ThisGuyHasTwoThumbs 对我来说,它显示的是答案中的50。你使用的是哪个PHP版本?对我来说似乎相当奇怪。你只是运行了我的代码还是已经将其集成到你的代码中了? - KhorneHoly
PHP7.2 :) 并集成到我的代码中 - 不过,我认为插件本身可能会引起一些问题.. 我可以尝试在插件之外进行操作,看看结果是否相同。 - treyBake
@ThisGuyHasTwoThumbs 版本与我的相同。也许你可以将我的代码复制粘贴到你的项目中的一个空文件中,作为测试和概念验证,以查看问题是在代码还是插件中。 - KhorneHoly

0

将您的“伪定界符”设置为像此示例中的“929292”这样的不常见模式。然后,您可以使用uksort仅遍历您的键。将“929292”替换为“。”,以便最终得到类似于“100.3”,“125.12”和“150.32”的内容。

现在,您不再局限于尝试进行数字和模式处理,而是可以使用好老的explode并手动比较。

此解决方案不关心数组中嵌套的内容,它只关心用于排序的键。

$options = [
  '1009292923' => '100cc',
  '12592929212' => '150cc',
  '5092929232' => '50cc'
];

$sorted = uksort($options, function($a, $b){
  $parts = explode('.',str_replace('929292', '.', $a));
  $acc = $parts[0];

  $parts = explode('.',str_replace('929292', '.', $b));
  $bcc = $parts[0];

  if($acc == $bcc) { return 0; }
  if($acc > $bcc) { return 1; }
  if($acc < $bcc) { return -1; }
});

var_dump($options);

编辑:在这里使用str_replace有点无意义,你可以直接使用“929292”作为分隔符在键上运行explode。


0

我不确定这是否是对我的问题的确切答案,因为它处理了问题但是以不同的方式。基本上,我已经将我的SQL更改为:

$sql = 'SELECT * FROM `ebay_mml`
        WHERE `UKM_CCM` IN ('. $where .')
        AND `UKM_Make` = :make 
        ORDER BY CAST(SUBSTR(`UKM_CCM`, INSTR(`UKM_CCM`, " ") + 1) AS UNSIGNED),
                 `UKM_Model`,
                 `UKM_StreetName`,
                 `Year`
        ASC;';

$where 是从 foreach 循环生成的变量:

foreach ($cc as $k => $v)
{
    $where .= ':'. $k .($k != end(array_keys($cc)) ? ', ' : '');

    $whereData[':'. $k] = $v;
}

这会一次性返回所有数据,所以现在我只需要循环遍历结果并计算迭代次数来构建键:

$i = 0;

foreach ($data as $result)
{
    # my sexy code
    $i++;
}

现在我的结果就像我想要的那样。

免责声明:虽然这解决了手头的问题,但它有点偏离最初提出的问题,因为它更像是一个MySQL解决方案,而不是按其键值对数组进行排序/分组。如果这个答案可以接受(如果可以,将删除免责声明部分),请告诉我。

感谢大家的帮助 :)


最重要的是,未来的用户会发现这个问题需要解决什么?刚刚重新阅读这个问题,我会说这是一个PHP数组排序问题,所以答案应该反映出这一点。虽然您的回答在您的情况下当然是正确的,但它使用了在问题中找不到的信息来解决它,因此对未来的用户没有帮助。 - Royal Wares
你可以开始一个新问题,并重新表述以更好地匹配你的答案解决的问题,然后在那里解决它。 - Royal Wares
@AlexanderDeSousa 是的,这就是我为什么认为这可能不是对原问题的确切回答的原因 :S 嗯,我现在先留着,很快再次审查一下 :) - treyBake

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