Сортировать / группировать массив по ключу

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. Таким образом, опции для 50cc с переданным счетчиком цикла 9 больше, чем 100cc.

(просто для ясности, пример вывода):

50cc Вариант 1
50cc Вариант 2
50cc Вариант 3
50cc Вариант 4
50cc Вариант 5
100cc Вариант 1
Вариант 100cc 2
Вариант 3 100cc
Вариант 6 объемом 50 куб.
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);

4

Решение

Вы можете отформатировать ключ, чтобы иметь 3 цифры для cc и 2 для опции …

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

который должен дать вам ключи 05031 а также 10002,

Тогда используйте SORT_STRING заставить его сортировать их как строки (хотя они будут сортировать и по числам)

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 => «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]]

0

Сделайте ваш «искусственный разделитель» необычным шаблоном, как «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 вроде бессмысленен, вы можете просто запустить разнесение непосредственно по ключу, используя «929292» в качестве разделителя.

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, а не сортировка / группировка массива по значению его ключа. Дайте мне знать, если этот ответ в порядке (если так, удалит сегмент отказа от ответственности) или нет.

Спасибо всем за помощь 🙂

0
По вопросам рекламы [email protected]