【问题标题】:Sort/group array by key按键排序/分组数组
【发布时间】:2019-03-27 10:38:48
【问题描述】:

TL;DR

按键排序/分组数组而不向数组添加另一个级别(由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 选项 1
50cc 选项 2
50cc 选项 3
50cc 选项 4
50cc 选项 5
100cc 选项 1
100cc 选项 2
100cc 选项 3
50cc 选项 6
50cc 选项 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);

【问题讨论】:

  • 您可以在 SQL 查询中对它们进行排序吗?
  • @MickaelLeger 它们是单独的 SQL 执行,所以不......虽然现在你提到它,也许我可以将我的 SQL 更改为使用WHERE IN ($values) - 请稍等! :)
  • 你能有一个非数字键吗? IE。 “50.3”?
  • @AlexanderDeSousa 这是一个好主意 - 但 JSON 方面似乎不允许“value.counter” - 它返回“值”。作为一个关键:/但很好的想法:)我会尝试使用_或其他东西:)
  • @ThisGuyHasTwoThumbs 没问题,我有一个使用 uksort 的书面解决方案,只要你的密钥能够有某种分隔符,我就可以做到。

标签: php mysql arrays sorting ksort


【解决方案1】:

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

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

这应该会给你0503110002的密钥。

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

【讨论】:

  • 理论上这看起来不错,但无论出于何种原因,它并没有消失 125cc 选项 X、100cc 选项、一些 50cc 选项、更多 125s 等:S
  • 您能否在您的问题中添加更多代码(包括 SQL),这可能有助于更多地理解它。
  • 你检查过echo json_encode($options); 给出的内容吗,你能补充一下吗(抱歉必须出去,需要几个小时)
  • 不是用你的代码说实话,用我原来的代码我做了 - 看看它给了我什么:)
【解决方案2】:

如果您可以将附加键添加到您的数组中,您可以创建一个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;
});

这个例子中这个函数的返回是:

数组:6 [▼ 0 => 数组:3 [▼ 0 => “富” 1 => “酒吧” "arrayKey" => 501] 1 => 数组:3 [▼ 0 => “富” 1 => “酒吧” "arrayKey" => 504] 2 => array:3 [▼ 0 => “富” 1 => “酒吧” "arrayKey" => 5045] 3 => array:3 [▼ 0 => “富” 1 => “酒吧” "arrayKey" => 1002] 4 => 数组:3 [▼ 0 => “富” 1 => “酒吧” "arrayKey" => 10045 ] 5 => 数组:3 [▼ 0 => “富” 1 => “酒吧” "arrayKey" => 1251 ] ]

【讨论】:

  • 我在上面试过了,按正确的数字意义排序​​(例如,100、125 首先显示,但 50 在最后:S)
  • @ThisGuyHasTwoThumbs 对我来说,它显示在答案中,第一个是 50。你运行的是什么 PHP 版本?对我来说似乎很奇怪。您是刚刚运行我的代码还是已经将它集成到您​​的代码中?
  • PHP7.2 :) 并集成到我的代码中 - 但是,我认为插件本身可能会导致一些问题.. 我可能会尝试在插件之外做,看看结果是否保持不变
  • @ThisGuyHasTwoThumbs 版本和我的一样。也许您可以将我的代码复制并粘贴到项目中的一个空文件中,作为测试和概念证明,以查看问题是出在代码还是您的插件中。
【解决方案3】:

在本例中将“人造分隔符”设为不常见的模式,例如“929292”。然后,您可以使用 uksort 只检查您的密钥。将“929292”替换为“。”所以你最终会得到像“100.3”、“125.12”和“150.32”这样的东西。

现在您不再局限于尝试使用数字和模式进行工作,您可以使用一个很好的旧式分解并手动比较。

这个解决方案不关心你的数组中嵌套了什么,它只关心排序的键。

$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 在这里毫无意义,您可以直接在键上运行explode,使用“929292”作为分隔符。

【讨论】:

    【解决方案4】:

    我不确定这是否是对我自己的问题的确切答案,因为它以不同的方式处理问题。本质上,我已经将我的 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 数组排序问题,所以答案应该反映这一点。虽然您的答案当然对您的情况是正确的,但它使用问题中未找到的信息来解决问题,因此不太可能对未来的用户有所帮助。
    • 您可以做的是开始一个新问题,重新措辞以更好地匹配您的答案解决的问题并在那里解决它。
    • @AlexanderDeSousa 是的,这就是为什么我认为这可能不是对原始问题的确切答案:S hm,我暂时离开并很快再次审查:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-15
    • 2018-11-11
    • 2018-09-12
    • 1970-01-01
    • 1970-01-01
    • 2023-03-18
    • 2010-10-28
    相关资源
    最近更新 更多