Reputation: 7101
I have the following array:
$array = [
'note' => [],
'year' => ['2011','2010', '2012'],
'type' => ['conference', 'journal', 'conference'],
];
And I use the following function to sort the array using the field type and another array:
function array_multisort_by_order(array $array, $by, array $order)
{
$order = array_flip($order);
$params[] = $array[$by];
foreach($params[0] as &$v) $v = $order[$v];
foreach($array as &$v) $params[] = &$v; unset($v);
call_user_func_array('array_multisort', $params);
return $array;
}
When I call the following function I get the following error:
$array = array_multisort_by_order($array, 'type', array('conference', 'journal'));
print_r($array['type']);
Error:
Warning: array_multisort(): Array sizes are inconsistent.
I know that arrays are inconsistent. Is there a better function to use?
Please check: codepad
Desired Output:
Array
(
[note] => Array
(
[0] =>
[1] =>
[2] =>
)
[year] => Array
(
[0] => 2011
[1] => 2012
[2] => 2010
)
[type] => Array
(
[0] => conference
[1] => conference
[2] => journal
)
)
Example 2:
Array
$array = [
'note' => ['test1', 'test2'],
'year' => ['2011', '2012'],
'type' => ['conference', 'journal', 'conference'],
];
Desired Result 2
Array
(
[note] => Array
(
[0] => test1
[1] =>
[2] => tes2
)
[year] => Array
(
[0] => 2011
[1] => 2012
[2] =>
)
[type] => Array
(
[0] => conference
[1] => conference
[2] => journal
)
)
Upvotes: 7
Views: 2382
Reputation: 47991
array_multisort()
. No return
is needed because all sorting actions are done via references.Code: (Demo)
function array_multisort_custom(array &$array, $columnKey, array $order)
{
// guard clause/condition
if (!array_key_exists($columnKey, $array)) {
throw new Exception('Nominated sorting column not found');
}
// pad rows to consistent size
$maxCount = max(array_map('count', $array));
array_walk($array, fn(&$row) => $row = array_pad($row, $maxCount, null));
// populate first sorting parameter with custom order array
$priority = array_flip($order);
$default = count($order);
foreach ($array[$columnKey] as $v) {
$params[0][] = $priority[$v] ?? $default;
}
// assign reference variables to parameter array for all rows
foreach ($array as &$row) {
$params[] = &$row;
}
array_multisort(...$params);
}
array_multisort_custom($array, 'type', ['conference', 'journal']);
var_export($array);
If the input array's first level keys were numeric, the above snippet can be compacted a little more and the last foreach()
removed (because numeric keys can be unpacked with the spread operator inside of array_multisort()
). Demo or even Demo
Related answer: Sort array using array_multisort() with dynamic number of arguments/parameters/rules/data
Upvotes: 0
Reputation: 70520
OK, so, one of the first solutions that comes to mind is adding in the empty values to make them consistent:
function array_multisort_by_order(array $array, $by, array $order)
{
$max = max(array_map('count',$array));
//or, alternatively, depending on input (if there are no 'complete' subarrays):
//$max = max(array_map(function($arr){return max(array_keys($arr));},$array))+1;
//ADDITION: negative numeric keys:
$min = min(array_map(function($arr){return min(array_keys($arr));},$array));
$width = $max - min(0,$min);
foreach($array as &$sub){
// $addin = array_diff_key(array_fill(0,$max,null),$sub);
// $addin changed for negative keys:
$addin = array_diff_key(array_combine(range($min,$max),array_fill(0,$width,null)),$sub);
$sub = $addin + $sub;
ksort($sub);
}
$order = array_flip($order);
$params[] = $array[$by];
foreach($params[0] as &$v) $v = $order[$v];
foreach($array as &$v) $params[] = &$v; unset($v);
call_user_func_array('array_multisort', $params);
//no closeures here:
//foreach($array as &$sub) $sub = array_filter(function($a){return !is_null($a);},$sub);
$filter = create_function('$a','return !is_null($a);');
foreach($array as &$sub) $sub = array_filter($sub,$filter);
return $array;
}
Upvotes: 3