1
\$\begingroup\$

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.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

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.

\$\endgroup\$
1
  • \$\begingroup\$ @mickmackusa yes, thanks for pointing out the mistake \$\endgroup\$
    – janos
    Commented Sep 16, 2017 at 6:18
1
\$\begingroup\$

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));
\$\endgroup\$
1
  • \$\begingroup\$ To fix an error and refine the code, use this: 3v4l.org/AWPd8 \$\endgroup\$ Commented Jan 28, 2021 at 1:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.