Skip to content

Instantly share code, notes, and snippets.

@zmilan
Last active October 10, 2019 17:32
Show Gist options
  • Save zmilan/40a9861116c00ac96e84005453c72a33 to your computer and use it in GitHub Desktop.
Save zmilan/40a9861116c00ac96e84005453c72a33 to your computer and use it in GitHub Desktop.
find the nearest key, value, and delta from provided array of data.
<?php
/**
* Example of usage and results
* $a = [ 7, -10, 13, 8, 4, -7.2, -12, -3.7, 3.5, -9.6, 6.5, -1.7, -6.2, 7 ];
* print_r(nearest(-3.1, $a)); // idx = 7, delta = 0.6, val = -3.7
* print_r(nearest(7, $a)); // idx = 0, delta = 0, val = 7
* print_r(nearest(0, $a)); // idx = 11, delta = 1.7, val = -1.7
*/
function nearest($needle, $haystack) {
// do some basic check of inputs
if (!is_array($haystack) || empty($haystack) || !is_numeric($needle)) {
return false;
}
// I feel lucky
$found = array_search($needle, $haystack, false);
if ($found !== false) {
return [
'idx' => $found,
'delta' => 0,
'value' => $needle
];
}
// digging deeper by binary search
asort($haystack); // sort values first
// check edge case: lover than the most low value
$firstKey = key($haystack);
if ($haystack[$firstKey] > $needle) {
return [
'idx' => $firstKey,
'delta' => abs($haystack[$firstKey] - $needle),
'value' => $haystack[$firstKey]
];
}
// check edge case: bigger than the most biggest value
$lastKey = key(array_slice($haystack, -1, 1, true)); // array_key_last($haystack); > php7.2
if ($haystack[$lastKey] < $needle) {
return [
'idx' => $lastKey,
'delta' => abs($needle - $haystack[$lastKey]),
'value' => $haystack[$lastKey]
];
}
// digging trough array
$orderedKeys = array_keys($haystack);
$start = 0;
$end = count($haystack) - 1;
$nearest = ['key' => null, 'delta' => null];
$mid = null;
while ($start <= $end) {
// find middle index
$mid = (int)floor(($start + $end) / 2);
if ($needle < $haystack[$orderedKeys[$mid]]) {
// move to the left side of the array
$end = $mid -1;
}
else {
// move to the right side of the array
$start = $mid + 1;
}
$delta = abs($haystack[$orderedKeys[$mid]] - $needle);
if (!isset($nearest['delta']) || $nearest['delta'] > $delta) {
$nearest['key'] = $mid;
$nearest['delta'] = $delta;
}
}
return [
'idx' => $orderedKeys[$nearest['key']],
'delta' => $nearest['delta'],
'val' => $haystack[$orderedKeys[$nearest['key']]]
];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment