Im trying to sort any array with array_multisort and everything is working great. However, based on conditions in my script, I need to change the options. So What I have s
You could try to use call_user_func_array. But I've never tried it on a built-in function before. Here is an example:
$dynamicSort = "$sort1,SORT_ASC,$sort2,SORT_ASC,$sort3,SORT_ASC";
$param = array_merge(explode(",", $dynamicSort), array($arrayToSort))
call_user_func_array('array_multisort', $param)
I had the same problem with this answer: "Argument #1 is expected to be an array or a sort flag"
For anyone having the same problem try this instead:
$dynamicSort = array(&$sort1, SORT_ASC, &$sort2, SORT_ASC, &$sort3, SORT_ASC);
$param = array_merge($dynamicSort, array(&$arrayToSort));
call_user_func_array('array_multisort', $param);
Note that i have used the reference to my variables "&$" instead of $. This works great in php 5.3 but may cause error in 5.2 due to a bug.
It is important to understand that the array sent to call_user_func_array() must consist only of references; it is not important whether the array itself is passed by reference. I spent the better part of a day troubleshooting this; the fact that the examples on the function page at php.net all used literal arrays led me to this page: php Bug #49353. Problem solved.
This doesn't seem to be very well (or consistently) documented, so here goes....
These DO NOT WORK (PHP 5.3.3):
$multisort_array = array($arr1, SORT_DESC, SORT_STRING, $arr2); // array of values
call_user_func_array('array_multisort', $multisort_array); // array passed by value
$multisort_array = array($arr1, SORT_DESC, SORT_STRING, $arr2); // array of values
call_user_func_array('array_multisort', &$multisort_array); // array passed by reference
$multisort_array = array(&$arr1, SORT_DESC, SORT_STRING, &$arr2); // non-constants by reference
call_user_func_array('array_multisort', $multisort_array); // array passed by value
$multisort_array = array(&$arr1, SORT_DESC, SORT_STRING, &$arr2); // non-constants by reference
call_user_func_array('array_multisort', &$multisort_array); // array passed by reference
These DO WORK:
$sort = array('desc' => SORT_DESC, 'string' => SORT_STRING);
$multisort_array = array(&$arr1, &$sort['desc'], &$sort['string'], &$arr2); // all by reference
call_user_func_array('array_multisort', $multisort_array); // array passed by value
$sort = array('desc' => SORT_DESC, 'string' => SORT_STRING);
$multisort_array = array(&$arr1, &$sort['desc'], &$sort['string'], &$arr2); // all by reference
call_user_func_array('array_multisort', &$multisort_array); // array passed by reference
From PHP5.6, you can use a variadic technique. Simply push all of your sorting data and sorting logic into an indexed array, then use the splat operator to unpack the parameters into array_multisort()
. Be sure to make the array that you wish to modify -- modifiable by reference before pushing it into the parameters array.
The pushing of parameters into $sortingParams
below can be written more succinctly as a single declaration, but I think it will be easier to conceptualize this way. These individual pushes would be suitable inside of an iterating process (e.g. foreach()
).
For every column of data used to sort the parent array, you may elect to push zero, one, or two additional elements to best signify the sorting logic.
Code: (Demo)
$array = [
['number' => 2, 'letter' => 'a', 'price' => 9.99],
['number' => 3, 'letter' => 'b', 'price' => 9.99],
['number' => 1, 'letter' => 'c', 'price' => 9.50],
['number' => 1, 'letter' => 'd', 'price' => 10],
['number' => 1, 'letter' => 'e', 'price' => 9.99],
];
$sortingParams[] = array_column($array, 'number'); // 1-dimensional
$sortingParams[] = SORT_ASC; // this is omittable as well because it is assumed (just for demo)
$sortingParams[] = array_column($array, 'price'); // 1-dimensional
$sortingParams[] = SORT_DESC;
$sortingParams[] = SORT_NUMERIC; // this is omittable as well because it is assumed (just for demo)
$sortingParams[] = &$array; // this is the actual master array which should be modified
array_multisort(...$sortingParams); // unpack with splat operator
var_export($array);
Output:
array (
0 =>
array (
'number' => 1,
'letter' => 'd',
'price' => 10,
),
1 =>
array (
'number' => 1,
'letter' => 'e',
'price' => 9.99,
),
2 =>
array (
'number' => 1,
'letter' => 'c',
'price' => 9.5,
),
3 =>
array (
'number' => 2,
'letter' => 'a',
'price' => 9.99,
),
4 =>
array (
'number' => 3,
'letter' => 'b',
'price' => 9.99,
),
)
This technique is super powerful if you have dynamic rules being passed to your process. In my case, I needed to collect filters from my DataTables UI and regenerate the data as a .csv. I merely needed to iterate through DataTable's order data and derive my set of rules - done.
I find this syntax much kinder on the eyes versus call_user_func_array()
.
Here is a more complex implementation: Sort array of associative arrays on multiple columns using specified sorting rules
To add onto the existing answers, just thought I would add a little something. For anyone passing the desired "sort by" as a comma-separated $_POST variable (or any comma-separated variable for that matter):
//$_POST["sort_by"] = "column_A DESC, column_B ASC, columns_C DESC";
$sort_bys = explode(",", $_POST["sort_by"]);
$dynamicSort = array();
foreach($sort_bys as $sort_by){
$sort_by2 = trim(str_replace('DESC','',$sort_by));
$direction = (strpos($sort_by, 'DESC') !== false)?SORT_DESC:SORT_ASC;
$$sort_by2 = array_column($array_to_sort, $sort_by2);
$dynamicSort[] = &$$sort_by2;
$dynamicSort[] = $direction;
$dynamicSort[] = SORT_NUMERIC; //or SORT_STRING or SORT_REGULAR ...
}
$param = array_merge($dynamicSort, array(&$array_to_sort));
call_user_func_array('array_multisort', $param);