Problem
I have the following input array:
$arr = array(
'water' => array(0, 1, 2, 3, 4, 5),
'apple' => array(1, 5, 3, 4, 0, 0),
'beer' => array(0, 0, 0, 0, 0, 0)
);
Now I want to know which element has largest sum, so I have the following code:
$arrWeight = array();
foreach ($arr as $key => $value)
{
$arrWeight[$key] = is_array($value) ? array_sum($value) : 0;
}
Then, I want to sort array based on weight. That’s easy.
arsort($arrWeight);
Now, I want to slice the original array ($arr
) based on generated weights and limit.
$arrAllowedKeys = array_slice($arrWeight, 0, self::METRICS_BREAKDOWN_LIMIT, true);
foreach ($arr as $key => $value)
{
if (!array_key_exists($key, $arrAllowedKeys))
{
unset($arr[$key]);
}
}
For example, if I have self::METRICS_BREAKDOWN_LIMIT
equals to 2, I’d expect the following output:
$arr = array(
'water' => array(0, 1, 2, 3, 4, 5),
'apple' => array(1, 5, 3, 4, 0, 0),
);
So, basically, the above solution is working, but I’m looking for better approach.
Solution
Your implementation seems fine.
The only improvement I see is to not preserve the ordering when you slice.
So instead of:
$arrAllowedKeys = array_slice($arrWeight, 0, self::METRICS_BREAKDOWN_LIMIT, true);
This is all you need:
$arrAllowedKeys = array_slice($arrWeight, 0, self::METRICS_BREAKDOWN_LIMIT);
The title of your question is “sorting and slicing array”,
but the input array is not sorted.
During the process, only the calculated array of weights is sorted,
the original is not.
If the input is this:
$arr = array(
'apple' => array(1, 5, 3, 4, 0, 0),
'water' => array(0, 1, 2, 3, 4, 5),
'beer' => array(0, 0, 0, 0, 0, 0)
);
Then the output will be:
$arr = array(
'apple' => array(1, 5, 3, 4, 0, 0),
'water' => array(0, 1, 2, 3, 4, 5)
);
In other words, the input array is sliced, but not sorted.
What happens to elements with the same weight, around the self::METRICS_BREAKDOWN_LIMIT
index? For example, if self::METRICS_BREAKDOWN_LIMIT = 1
, and the initial array is this:
$arr = array(
'water' => array(1, 2, 3),
'apple' => array(1, 2, 3),
'beer' => array(1, 2, 3)
);
Which record will be in the result? It depends on whether arsort
is using a stable sorting algorithm (not so easy to find in the docs of arsort
). If arsort
is stable, insertion order will be preserved, and the answer will be water
. Otherwise it can be any of these. It would be a good idea to add a comment in the code about this.
One you have the $arrAllowedKeys
, you can simply use array_intersect_key
to get the final array, instead of looping through $arr
.
array_intersect_key($arr, array_fill_keys($arrAllowedKeys, null));
You might use array_map
rather than the initial foreach
loop.
$arrWeight = array_map($arr, function($value) {
return is_array($value) ? array_sum($value) : 0;
});
$arr = [
'water' => [0, 1, 2, 3, 4, 5],
'apple' => [1, 5, 3, 4, 0, 0],
'beer' => [0, 0, 0, 0, 0, 0]
];
$arrWeight = array_map($arr, function($value) {
return is_array($value) ? array_sum($value) : 0;
});
arsort($arrWeight);
$arrAllowedKeys = array_slice($arrWeight, 0, self::METRICS_BREAKDOWN_LIMIT, true);
$arr = array_intersect_key($arr, array_fill_keys($arrAllowedKeys, null));