简体   繁体   English

在数组中查找具有最接近值的对象

[英]Find object in array with closest value

I need to get an object in an array by the closest value.我需要通过最接近的值获取数组中的对象。 Let me explain it by an example:让我通过一个例子来解释它:

const data = [
  { age: 52 },
  { age: 53 },
  { age: 54 },
  { age: 60, some: 'data' },
  { age: 66, something: 'else' },
  { age: 72 },
  { age: 78 },
  { age: 84 }
]

I do get the object by using data.find((d)=> d.age === 60) .我确实通过使用data.find((d)=> d.age === 60)来获取对象。 But I do not get an result if the age is 61 .但是如果年龄是61我不会得到结果。 In this case I would like to get the same object.在这种情况下,我想获得相同的对象。

For 64 the next object ( { age: 66, something: 'else' } ) should be returned.对于64 ,应该返回下一个对象( { age: 66, something: 'else' } )。

As you can see the age value is not linear.如您所见,年龄值不是线性的。

You can find the difference between all the numbers and whichever one is closest to zero will be your result, to achieve this I have used .reduce() with Math.abs()你可以找到所有的区别的数量和哪一个是最接近零将是你的结果,要达到这个我已经使用.reduce()Math.abs()

 const data = [ { age: 52 }, { age: 53 }, { age: 54 }, { age: 60 }, { age: 66 }, { age: 72 }, { age: 78 }, { age: 84 } ]; const getAge = (data, target) => data.reduce((acc, obj) => Math.abs(target - obj.age) < Math.abs(target - acc.age) ? obj : acc ); console.log(getAge(data, 61)); // {age: 60} console.log(getAge(data, 50)); // {age: 52} console.log(getAge(data, -1)); // {age: 52} console.log(getAge(data, 90)); // {age: 84}

This will also work for more generalized objects that have additional properties other than just age .这也适用于具有除age之外的其他属性的更通用的对象。

Here is a fully abstract approach to your problem:这是解决您的问题的完全抽象的方法:

 // Saves up vertical space const data = JSON.parse(`[{"age":52},{"age":53},{"age":54},{"age":60},{"age":66},{"age":72},{"age":78},{"age":84}]`); function getClosestValue(list, getDifference) { var smallestDiff = Infinity; return list.reduce(function(closestValue, currentValue, index) { var newDifference = Math.abs(getDifference(currentValue)); if (!index) return smallestDiff = newDifference, currentValue; return smallestDiff = Math.min(smallestDiff, newDifference), newDifference === smallestDiff ? currentValue : closestValue; }); } function getClosestAge(list, age) { return getClosestValue(list, function(listValue) { return listValue.age - age; }); } console.log(getClosestAge(data, 65));

If it's always sorted you can instead use some :如果它总是排序,你可以使用some

 // Saves up vertical space const data = JSON.parse(`[{"age":52},{"age":53},{"age":54},{"age":60},{"age":66},{"age":72},{"age":78},{"age":84}]`); function getClosestValue(list, getDifference) { var smallestDiff = Infinity; var closestValue; list.some(function(currentValue, index) { var newDifference = Math.abs(getDifference(currentValue)); if (!index) return smallestDiff = newDifference, closestValue = currentValue, false; if (smallestDiff > newDifference) return smallestDiff = newDifference, closestValue = currentValue, false; else if (smallestDiff !== newDifference) return true; }); return closestValue; } function getClosestAge(list, age) { return getClosestValue(list, function(listValue) { return listValue.age - age; }); } console.log(getClosestAge(data, 65));

Assume, that your list ist not Sorted, and you do not want to sort your list.假设您的列表未排序,并且您不想对列表进行排序。 So you can pick the first object, iterate through your list and check if you get an item, which fits your requiremnt more than your currently picked item.所以你可以选择第一个对象,遍历你的列表并检查你是否得到一个项目,它比你当前选择的项目更符合你的要求。 If so, you just replace your item with the better one.如果是这样,您只需用更好的物品替换您的物品。

eg例如

var data = [/*...*/];
var find_age = 64;           // input
var best_item = data[0];     // pick any item as best item
for (var i = 1; i < data.length; i++) {

  // does date[i] match the requirement better than best_item?
  if (Math.abs (best_item.age - find_age) > Math.abs (data[i].age - find_age)) {

    // it does ... so update best_item
    best_item = data[i];
  }
}

// best_item stores the item which matches your requirement most.

If your dataset is sorted, you can optimize your runtime.如果您的数据集已排序,则可以优化运行时。

You can just sort the array by difference to lookup age:您可以通过差异对数组进行排序以查找年龄:

 const lookupAge = 61 const data = [ { age: 52 }, { age: 53 }, { age: 54 }, { age: 60 }, { age: 66 }, { age: 72 }, { age: 78 }, { age: 84 } ] const result = data .map(d => d.age) .sort((a, b) => Math.abs(a - lookupAge) - Math.abs(b - lookupAge)) console.log('result', result)

 const data = [ { age: 52 }, { age: 53 }, { age: 54 }, { age: 60 }, { age: 66 }, { age: 72 }, { age: 78 }, { age: 84 } ]; const find = 64; const result = data.map(({ age }) => age).reduce((best, el, index) => { if (Math.abs(find - el) < Math.abs(find - best)) { return el; } return best; }, data[0].age) console.log(result)

You can find the minimum difference by subtracting the given number from every element and take the absolute value and then do both higher lookup and lower lookup您可以通过从每个元素中减去给定的数字并取绝对值来找到最小差异,然后进行更高的查找和更低的查找

it will also consider when there are 2 different closest values它还会考虑何时有 2 个不同的最接近值

 const data = [ { age: 52 }, { age: 53 }, { age: 55 }, { age: 60 }, { age: 66 }, { age: 72 }, { age: 78 }, { age: 84 } ] function minimum(given){ //let given=54 //find the mimimun different let closest_diff=Math.min(...data.map(a=>Math.abs(a.age-given))) //for lower closest number let x1=data.find(a=>a.age===given-closest_diff); //for highter closest number let x2=data.find(a=>a.age===given+closest_diff); //filter the number which are in array above console.log(...new Set([x1,x2].filter(x=>x))); } minimum(52); //52 minimum(54); //53 and 55 minimum(63); //60 and 66 minimum(75); //72 and 78 minimum(77); //78

With sorted data, you could take the one with the greatest value as start value an iterate from the beginning and stop the iteration if the delta grows.对于已排序的数据,您可以将具有最大值的数据作为起始值从头开始迭代,如果增量增长则停止迭代。

 var data = [{ age: 52 }, { age: 53 }, { age: 54 }, { age: 60 }, { age: 66 }, { age: 72 }, { age: 78 }, { age: 84 }], result = data[data.length - 1], age = 61; data.some((o) => { if (Math.abs(age - o.age) >= Math.abs(age - result.age)) return true; result = o; }); console.log(result);

I made a lil' snippet code to show you the way I would do this.我做了一个小片段代码来向你展示我会这样做的方式。 This creates to use a findClosest method on any array of object, that expects an attribute name and a value.这将创建在任何对象数组上使用findClosest方法,该方法需要一个属性名称和一个值。 The function will then return the element of the array that has the closest value to the given attribute.然后,该函数将返回数组中与给定属性值最接近的元素。 It could be improved but this works pretty well.它可以改进,但效果很好。

 document.addEventListener("DOMContentLoaded", function() { const listElem = document.getElementById('list'); const closestElem = document.getElementById('closest'); data.forEach(elem => { const listElemEntry = document.createElement('li'); listElemEntry.innerHTML = elem.age; listElem.appendChild(listElemEntry); }); const closest = data.findClosest('age', 80); closestElem.innerHTML = closest; }); const data = [ { age: 52 }, { age: 53 }, { age: 54 }, { age: 60 }, { age: 66 }, { age: 72 }, { age: 78 }, { age: 84 } ]; Array.prototype.findClosest = function(attr, value) { const closestElem = { diff: Infinity, index: -1 }; this.forEach((elem, index) => { const diff = Math.abs(elem[attr] - value); if (diff < closestElem.diff) { closestElem.diff = diff; closestElem.index = index; } }); return this[closestElem.index][attr]; }
 <h2>Elements list</h2> <ul id="list"></ul> <h2>Closest element</h2> <pre id="closest"></pre>

You can find closest item of array with minimum value of differences like below;您可以找到最接近的数组项,其差异最小值如下所示;

function getClosest(data, x) {
    if (data.length == 0) {
        return null;
    }
    var index = 0;
    var difference = Number.MAX_SAFE_INTEGER;
    for(var i = 0; i<data.length;i++) {    
        if (i < data.length) {
            var differ =  Math.abs(data[i].age - x);           
            if(differ < difference) {
                difference = differ;
                index = i;    
            }        
        }   
    }
    return data[index];
}

Usage:用法:

getClosest(data, 64)
Suppose array isn't sorted. Following function returns result. If it find value that is equal to search value, it stops searching, so it is a small gain in performance.

function minDiff(data, val) {
    let res = null;
    let n = data.length;
    let diffGet = (val1, val2) => Math.abs(val1 - val2);

    if (n>0) {
        res = data[0];
        let diff = diffGet(res.age, val);
        let i = 1;
        while ( diff>0 && i<n ) {
            if (diffGet(data[i].age, val) < diff) {
                res = data[i];
                diff = diffGet(res.age, val);                
            }
            i++;            
        }        
    }

    return res;

}

This is a functional approach to your problem with currying:这是解决柯里化问题的一种功能方法:

 const data = [ { age: 52 }, { age: 53 }, { age: 54 }, { age: 60, some: "data" }, { age: 66, something: "else" }, { age: 72 }, { age: 78 }, { age: 84 } ]; const indexOfSmallest = (array) => { if (array.length === 0) { throw new Error("Empty array, expects at least one element"); } return array.reduce((lowest, next, index) => { if (next < array[lowest]) { return index; } return lowest; }, 0); }; const getClosestIndex = (numbers, referenceNumber) => { const diff = numbers.map(n => Math.abs(referenceNumber - n)); return indexOfSmallest(diff); }; const createGetClosestIndex = (numbers) => (number) => getClosestIndex(numbers, number); const createGetClosestPerson = (people) => { return (targetAge) => { const numbers = people.map(d => d.age); const index = createGetClosestIndex(numbers)(targetAge); return people[index]; }; }; const getClosest = createGetClosestPerson(data); console.log(getClosest(1), getClosest(64));

A general purpose version of @nick-parsons excellent answer... @nick-parsons 优秀答案的通用版本...

 function closest (num, arr, key=null) { if (key==null) { return arr.reduce((a, b) => Math.abs(num - b) < Math.abs(num - a) ? b : a); } return arr.reduce((a, b) => { if (b[key] == null) return a; if (a[key] == null) return b; return Math.abs(num - b[key]) < Math.abs(num - a[key]) ? b : a; }); } let arr = [ {speed: 0.1}, {speed: 0.4}, {speed: 1} ] console.log( closest(0.5, arr, "speed").speed ) // output: 0.4 arr = [ 0.1, 0.4, 1 ] console.log( closest(0.9, arr) ) // output: 1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM