I\'m trying to do the same as MySQL query
SELECT * FROM table ORDER BY field1, field2, ...
but with php and a multidimensional array:
This will do the job, thanks for contributions!
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"));
This code is a little ugly, though, I believe it can be better - maybe a class to solve the issue of passing the array with the keys to the "cmp" function. But it's a start point, you can use it with any number of keys to sort.
Fundamentally we're going to use the same approach as explained here, we're just going to do it with a variable number of keys:
/**
* 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'));
That's about equivalent to an SQL ORDER BY foo, bar, baz
.
If of course each key requires a different kind of comparison logic and you cannot use a general strcmp
or -
for all keys, you're back to the same code as explained here.
Here's some code I wrote to do something similar:
uasort($array,function($a,$b) {
return strcmp($a['launch'],$b['launch'])
?: strcmp($a['tld'],$b['tld'])
?: strcmp($a['sld'],$b['sld']);
});
It kind of abuses the fact that negative numbers are truthy (only zero is falsy) to first compare launch
, then tld
, then sld
. You should be able to adapt this to your needs easily enough.
PHP7.4's arrow syntax eliminates much of the code bloat which was previously necessary to bring your column orders into the usort()
scope.
Code: (Demo)
$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);
I reckon this is a very elegant and concise way to script the task. You generate the nominated column values from $a
and $b
as separate arrays and write the spaceship operator between them.
Without the arrow syntax, the snippet gets a little more chunky.
Code: (Demo)
$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);
The spaceship operator will walk through corresponding pairs of data ([0]
vs [0]
, then [1]
vs [1]
, and so on) until it reaches a non-zero evaluation or until it exhausts the comparison arrays.