Как отсортировать многомерный массив по ключам умножения?
Я пытаюсь сделать то же самое, что и запрос MySQL "SELECT * FROM таблицы ORDER BY field1, field2, ..." с многомерным массивом:
$Test = array(
array("a"=>"004", "n"=>"03"),
array("a"=>"003", "n"=>"02"),
array("a"=>"001", "n"=>"02"),
array("a"=>"005", "n"=>"01"),
array("a"=>"001", "n"=>"01"),
array("a"=>"004", "n"=>"02"),
array("a"=>"003", "n"=>"01"),
array("a"=>"004", "n"=>"01")
);
function msort(&$array, $keys){
array_reverse($keys);
foreach($keys as $key){
uasort($array, sortByKey);
}
//
function sortByKey($A, $B){
global $key;
$a = $A[$key];
$b = $B[$key];
if($a==$b) return 0;
return ($a < $b)? -1 : 1 ;
}
}
//
msort($Test, array("a","n"));
//
foreach($Test as $t){
echo('<p>'.$t["a"].'-'.$t["n"].'</p>');
}
Моя теория такова: если я сортирую кратные разы, образуя менее важный ключ по самому важному ключу, я закажу такой запрос mySQL.
Но php возвращает "Предупреждение: uasort() ожидает, что параметр 2 является допустимым обратным вызовом, функция" sortByKey "не найдена или недопустимое имя функции в /Library/WebServer/Documents/www/teste.array_sort.php в строке 23" (uasort линия)
Это простая функция заказа, что мне не хватает?
5 ответов
По сути, мы будем использовать тот же подход, который описан здесь, мы просто сделаем это с переменным количеством ключей:
/**
* Returns a comparison function to sort by $cmp
* over multiple keys. First argument is the comparison
* function, all following arguments are the keys to
* sort by.
*/
function createMultiKeyCmpFunc($cmp, $key /* , keys... */) {
$keys = func_get_args();
array_shift($keys);
return function (array $a, array $b) use ($cmp, $keys) {
return array_reduce($keys, function ($result, $key) use ($cmp, $a, $b) {
return $result ?: call_user_func($cmp, $a[$key], $b[$key]);
});
};
}
usort($array, createMultiKeyCmpFunc('strcmp', 'foo', 'bar', 'baz'));
// or
usort($array, createMultiKeyCmpFunc(function ($a, $b) { return $a - $b; }, 'foo', 'bar', 'baz'));
Это примерно эквивалентно SQL ORDER BY foo, bar, baz
,
Если, конечно, каждый ключ требует различного рода логики сравнения, и вы не можете использовать общий strcmp
или же -
для всех ключей вы вернулись к тому же коду, который описан здесь.
Вот код, который я написал, чтобы сделать нечто подобное:
uasort($array,function($a,$b) {
return strcmp($a['launch'],$b['launch'])
?: strcmp($a['tld'],$b['tld'])
?: strcmp($a['sld'],$b['sld']);
});
Это как бы злоупотребляет тем фактом, что отрицательные числа являются правдивыми (только ноль - ложными), чтобы сначала сравнить launch
, затем tld
, затем sld
, Вы должны быть в состоянии приспособить это к вашим потребностям достаточно легко.
Синтаксис стрелок PHP7.4 устраняет большую часть раздутого кода, который ранее был необходим для переноса порядка столбцов в usort()
объем.
Код: (Демо)
$orderBy = ['a', 'n'];
usort($Test, fn($a, $b) =>
array_map(fn($v) => $a[$v], $orderBy)
<=>
array_map(fn($v) => $b[$v], $orderBy)
);
var_export($Test);
Я считаю, что это очень элегантный и лаконичный способ написания задачи. Вы генерируете назначенные значения столбца из$a
а также $b
в виде отдельных массивов и записать между ними оператор космического корабля.
Без синтаксиса со стрелкой фрагмент становится немного короче.
Код: (Демо)
$orderBy = ['a', 'n'];
usort($Test, function($a, $b) use ($orderBy) {
return
array_map(function($v) use ($a){
return $a[$v];
}, $orderBy)
<=>
array_map(function($v) use ($b){
return $b[$v];
}, $orderBy);
});
var_export($Test);
Оператор космического корабля просмотрит соответствующие пары данных ([0]
против [0]
, тогда [1]
против [1]
и т. д.), пока он не достигнет ненулевой оценки или пока не исчерпает массив сравнения.
Исправил вашу функцию
$Test = array(
array("a"=>"004", "n"=>"03"),
array("a"=>"003", "n"=>"02"),
array("a"=>"001", "n"=>"02"),
array("a"=>"005", "n"=>"01"),
array("a"=>"001", "n"=>"01"),
array("a"=>"004", "n"=>"02"),
array("a"=>"003", "n"=>"01"),
array("a"=>"004", "n"=>"01")
);
function msort(&$array, $keys){
foreach(array_reverse($keys) as $key)
usort($array, Fn ($A, $B) => $A[$key]<=>$B[$key]);
}
msort($Test, array("a","n"));
foreach($Test as $t){
echo('<p>'.$t["a"].'-'.$t["n"].'</p>');
}
Результат
001-01
001-02
003-01
003-02
004-01
004-02
004-03
005-01
или с направлением сортировки
function msort(&$array, $keys){
foreach(array_reverse($keys) as $key=>$sort)
usort($array,Fn($a,$b) => $sort == SORT_DESC ? $b[$key]<=>$a[$key]:$a[$key]<=>$b[$key]);
}
msort($Test, array("a"=>SORT_DESC,"n"=>SORT_ASC));
Результат
005-01
004-01
004-02
004-03
003-01
003-02
001-01
001-02
Это сделает работу, спасибо за вклад!
function mdsort(&$array, $keys){
global $KeyOrder;
$KeyOrder = $keys;
uasort($array, cmp);
}
function cmp(array $a, array $b) {
global $KeyOrder;
foreach($KeyOrder as $key){
$res = strcmp($a[$key], $b[$key]);
if($res!=0) break;
}
return $res;
}
//
mdsort($Test, array("a","n"));
Этот код немного уродлив, хотя, я думаю, что он может быть лучше - может быть, класс, чтобы решить проблему передачи массива с ключами в функцию "cmp". Но это отправная точка, вы можете использовать ее с любым количеством ключей для сортировки.