'Filter associative array to keep elements when their value is greater than their neighboring elements' values

I have an associative array from which I need a new array to be created. I am targeting the peak values for the new array. For a value to be selected to the new array, it needs to be higher than both the previous and the next value in the array.

I have searched the net and found a function that could handle the situation for indexed arrays - but not for associative arrays. Here is what I would like:

$array = array('a' => 0, 'b' => 2, 'c' => 1, 'd' => 2, 'e' => 3);
$result = array('b' => 2, 'e' => 3);

The function usable for indexed arrays look like this:

$a = array(0, 2, 1, 2, 3);
$b = array_filter($a, function($v, $k) use($a) {
  return $k > 0 && $v > $a[$k-1] && $k + 1 < count($a) && $v > $a[$k+1];
}, ARRAY_FILTER_USE_BOTH );

This function doesn't include the last value either.



Solution 1:[1]

This can be done with zero iterated function calls.

Before looping declare a lookup array by assigning indices to the input array's keys -- array_keys().

Then in the loop use conditional fallback values and null coalescing when an attempt to access a non-existent element occurs.

  1. ($i ? $array[$keys[$i - 1]] : $array[$key] - 1) means: if $i is not zero, then access the value from the input array by its key which is one position before the current key. Otherwise, $i equals zero which means there is no earlier value so fallback to the current value minus one to guarantee it will be less in the comparison.

  2. ($array[$keys[$i + 1] ?? -1] ?? $array[$key] - 1) means: try to access the next key in the lookup array, if it does not exist, use negative one which is guaranteed not to exist. By effect, when $i is that last index in the loop, there will be no next key, so fallback to the current value minus one again.

This should always outperform any script that makes iterated function calls.

Code: (Demo)

$array = ['a' => 0, 'b' => 2, 'c' => 1, 'd' => 2, 'e' => 3];

$keys = array_keys($array);
foreach ($keys as $i => $key) {
    if ($array[$key] > ($i ? $array[$keys[$i - 1]] : $array[$key] - 1)
        && $array[$key] > ($array[$keys[$i + 1] ?? -1] ?? $array[$key] - 1)
    ) {
        $result[$key] = $array[$key];
    }
}
var_export($result);

Output:

array (
  'b' => 2,
  'e' => 3,
)

Alternatively, if you want to mutate the input array, you can call unset() in the loop and simply invert the conditional logic.

Solution 2:[2]

I worked @splash58. Thank you. I have none the less tried something myself which also worked - but I don't know which solution is most effective.

$result = array();
$value1 = NULL;

foreach (array_reverse($array) as $key => $value) {
  if ($value > $value1) {
    $result[$key] = $value;
  }
  $value1 = $value;
}

and is '$value1 = NULL' this necessary?

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 mickmackusa
Solution 2 SVL