I have this:
var scores=[0.7, 1.05, 0.81, 0.96, 3.2, 1.23];
What's the more readable way to return the indexes of the nearest values to another variable?
For instance:
With variable = 1 Should return { low: 3, high: 1 }
I have this:
var scores=[0.7, 1.05, 0.81, 0.96, 3.2, 1.23];
What's the more readable way to return the indexes of the nearest values to another variable?
For instance:
With variable = 1 Should return { low: 3, high: 1 }
- 6 Iterate over the array, pare values and record indices? If by "best" you mean something more elaborated, maybe build an interval tree? en.wikipedia/wiki/Interval_tree – Felix Kling Commented Sep 15, 2014 at 18:13
- less elaborate, but simple. i was trying Array.reduce returning an object but could not. – rnrneverdies Commented Sep 15, 2014 at 18:21
7 Answers
Reset to default 5Almost as simple but faster (O(n)) than sort:
const nearest = (arr, n) => arr.reduce((r, x) => ({
lo: ((x < n) && (x > r.lo) ? x : r.lo),
hi: ((x > n) && (x < r.hi) ? x : r.hi)
}), { lo: -Infinity, hi: Infinity })
const mapIndexOf = (obj, lookup) => Object.keys(obj).reduce(
(a, v) => ({ ...a, [v]: lookup.indexOf(obj[v]) }), {}
)
const scores = [0.7, 1.05, 0.81, 0.96, 3.2, 1.23]
console.log(mapIndexOf(nearest(scores, 1), scores))
Slow (O(n*log(n))
and simple:
const nearest = (arr, val) => (sorted => (indexOfVal => ({
lo: sorted[indexOfVal - 1],
hi: sorted[indexOfVal + 1]
}))(sorted.indexOf(val)))([...arr, val].sort())
const mapIndexOf = (obj, lookup) => Object.keys(obj).reduce(
(a, v) => ({ ...a, [v]: lookup.indexOf(obj[v]) }), {}
)
const scores = [0.7, 1.05, 0.81, 0.96, 3.2, 1.23]
console.log(mapIndexOf(nearest(scores, 1), scores))
This way:
var lower = function(a,b){return a.element > b.element ? b : a; };
var higher = function(a,b){return a.element > b.element ? a : b; };
var withIndex = function(element,index){ return {element: element, index: index}; };
var nearest = function(array, limit) {
var lowerValues = array.map(withIndex).filter(function(a){ return a.element<limit });
var higherValues = array.map(withIndex).filter(function(a){ return a.element>limit });
return {
low: lowerValues.reduce(higher).index,
high: higherValues.reduce(lower).index
};
}
var scores=[0.7, 1.05, 0.81, 0.96, 3.2, 1.23];
var lowIndex = 0;
var highIndex = 0;
var currentLow = 0;
var currentHigh = 0;
var temp = 0;
var variable = 2;
for(var i = 0; i < scores.length; i++)
{
temp = variable - scores[i];
if((currentLow == 0) && (temp > 0))
{
currentLow = temp;
}
if((currentHigh == 0) && (temp < 0))
{
currentHigh = temp;
}
if((temp >= currentHigh) && (temp <= 0))
{
highIndex = i;
currentHigh = temp;
}
if((temp <= currentLow) && (temp >= 0))
{
lowIndex = i;
currentLow = temp;
}
}
window.alert("Low:" + lowIndex + " High:" + highIndex);
This code works and you can see the logic of whats going on.
Loop over the array, subtract the value from your variable, pare, then record the closest values. Here's a quick example:
var value = 1;
var scores = [0.7, 1.05, 0.81, 0.96, 3.2, 1.23];
var ret = {};
for(var i = 0, len = scores.length; i < len; i++){
var p = scores[i] - value;
if(p > 0){
if(!ret.high){
ret.high = i;
}
else if(scores[i] < scores[ret.high]){
ret.high = i;
}
}
else if(p < 0){
if(!ret.low){
ret.low = i;
}
else if(scores[i] > scores[ret.low]){
ret.low = i;
}
}
else{
ret = {
low: i,
high: i
};
break;
}
}
document.getElementById('result').innerHTML = 'high: '+ret.high+' low: '+ret.low;
<div id="result"></div>
// for storing greater values and their indeces
var gtVals = {
val : [],
ind : []
};
// for storing lesser values and their indeces
var ltVals = {
val : [],
ind : []
}
var scores=[0.7, 1.05, 0.81, 0.96, 3.2, 1.23];
function highLow(value){
var val = parseFloat(value);
for(var i = 0; i < scores.length ; i++){
if(scores[i] > val ){
gtVals.val.push(scores [i] - val );
gtVals.ind.push(i );
}
else{
ltVals.val.push(val - scores[i] );
ltVals.ind.push(i );
}
}
var higherindex = gtVals.ind[gtVals.val.indexOf((Math.min.apply(Math, gtVals.val)))];
var lowerindex = ltVals.ind[ltVals.val.indexOf((Math.min.apply(Math, ltVals.val)))];
return {
low: lowerindex,
high : higherindex
};
}
console.log(highLow(1));
http://jsfiddle/2q572hxj/3/
For people who love perl, assembler or regular expressions, here a solution near to a single-liner:
var pV = 1;
var scores=[0.7, 1.05, 0.81, 0.96, 3.2, 1.23];
for(var h=0,l=0,i=0,tmp=0,lV=Number.MAX_VALUE,hV=lV; i < scores.length; i++) {
tmp=pV-scores[i];tmp>0 && tmp<lV ? (l=i,lV=tmp) : tmp<0 && -tmp<hV ? (h=i,hV=-tmp) : 0; }
l (lowercase "L") holds the index of the lowest value, h (lowercase "H") holds the index of the highest value. Have fun :)