I have a four-level multidimensional array. I need to sort in ascending order (ASC) the numeric "leaves" in order to calculate the median of the values.
I tried array_walk_recursive()
, array_multisort()
, usort()
, etc. but was unable to find a working solution.
Here's a schematic of the array:
(
[2017-05-01] => Array
(
[DC] => Array
(
[IT] => Array
(
[0] => 90
[1] => 0
)
[DE] => Array
(
[0] => 18
[1] => 315
[2] => 40
[3] =>
[4] => 69
)
[Other] => Array
(
[0] => 107
[1] => 46
[2] =>
[3] =>
[4] => 27
[5] => 22
)
)
)
)
This will output the deepest subarrays' median values using the input array's structure.
I'm including hard-casting of median values (one or both in a subset) as integers in the event that the value(s) are empty strings. I'll also assume that you will want 0
as the output if a subset is empty.
Create a new array in a functional style: Demo
var_export(
array_map(
fn($dateSet) => array_map(
fn($dcSet) => array_map(
function ($set) {
if (!$set) {
return 0;
}
sort($set);
$count = count($set);
$mid = intdiv($count, 2);
if ($count & 1) {
return $set[$mid];
}
return ((int)$set[$mid - 1] + (int)$set[$mid]) / 2;
},
$dcSet
),
$dateSet
),
$array
)
);
Or modify the input array by reference in a classic loop. Demo
foreach ($array as &$dateSet) {
foreach ($dateSet as &$dcSet) {
foreach ($dcSet as &$set) {
if (!$set) {
$set = 0;
continue;
}
sort($set);
$count = count($set);
$mid = intdiv($count, 2);
if ($count & 1) {
$set = $set[$mid];
continue;
}
$set = ((int)$set[$mid - 1] + (int)$set[$mid]) / 2;
}
}
}
var_export($array);
Or modify the input array by reference with array_walk()
. Demo
array_walk(
$array,
fn(&$dateSet) => array_walk(
$dateSet,
fn(&$dcSet) => array_walk(
$dcSet,
function (&$set) {
if (!$set) {
$set = 0;
return;
}
sort($set);
$count = count($set);
$mid = intdiv($count, 2);
if ($count & 1) {
$set = $set[$mid];
return;
}
$set = ((int)$set[$mid - 1] + (int)$set[$mid]) / 2;
}
)
)
);
var_export($array);
*for the record, $count & 1
is a bitwise comparison that determines if the value is odd without performing arithmetic (and is the most efficient way of performing this check within php).
*also, if you wanted to simply overwrite the values of the input array, you could modify by reference by writing &
before $lv1
, $lv2
, and $lv3
in the foreach declarations then save the median value to $lv3
. Demo The benefit in doing so removes key declarations and making your code more brief.