在数组中找到最接近给定值的对象

7

我需要通过最接近的值获取数组中的对象。让我通过一个例子来解释一下:

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

我使用data.find((d)=> d.age === 60)获取对象。但是如果年龄为61,则无法获得结果。在这种情况下,我想获取相同的对象。

对于64,应返回下一个对象({ age: 66, something: 'else' })。

正如您所看到的,年龄值不是线性的。


数组是否总是有序的? - nick zoum
@nickzoum 是的,但每个年龄值之间的差异并不总是相同的。 - user3142695
当输入63时,最接近的值应该是66还是60? - sumit
12个回答

15

您可以找到所有数字之间的差异,最接近零的数字将是您的结果,为了实现这一点,我使用了.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}
这对于具有除了age之外的其他属性的更一般化对象也适用。


如果有两个最接近的值,比如(52,54,60),并且输入是53,会发生什么? - sumit
@sumit 它将取找到的第一个最接近的(所以在这种情况下是52) - Nick Parsons
假设数据对象除了 age 之外还有更多字段,我想获取完全匹配的对象...我需要更改什么? - user3142695
@user3142695 如果您查看第二个示例代码片段(默认情况下隐藏),它使用 ...rest(这是一种在解构时用于获取所有其他属性的语法)。这将允许您保留其他属性。 - Nick Parsons

2
这里是一个完全抽象的解决方案:
最初的回答:

// 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));

如果它总是被排序的,您可以改用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));


0

你可以通过差值来排序数组以查找年龄:

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)


0

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)


0

您可以通过从每个元素中减去给定数字并取绝对值,然后进行较高的查找和较低的查找来找到最小差异。

它还将考虑当存在两个不同的最接近值时

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


0

有了排序数据,您可以将具有最大值的值作为起始值并从开头迭代,如果增量增加,则停止迭代。

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);


当输入63时,最接近的值应该是66还是60? - sumit
2
我选择较小的值 60。如果将条件语句中的等号去掉,那么返回值将变为 66 - Nina Scholz
我认为我们需要考虑两者,不确定 OP 想要什么,他的问题不够清晰。 - sumit

0
我写了一小段代码来展示我会如何做这件事。它创建了一个使用findClosest方法的对象数组,该方法期望属性名称和值。然后函数将返回具有最接近给定属性值的数组元素。它可以改进,但这个方法已经相当有效了。

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>


0
您可以按照以下方式找到差异最小的数组中最接近的项;
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];
}

使用方法:

getClosest(data, 64)

0

这是一种使用柯里化的功能性方法来解决您的问题:

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));


0

这是@nick-parsons优秀答案的通用版本...

/**
 * Find the closest number in an array.
 *
 * @param Number                needle    The number we're looking for.
 * @param Array<Number|Object>  haystack  An array to search.
 * @param String                [key]     We're searching an array of objects. 
 *                                        Use this key to find the number in each object.
 * @return Number|Object
 */
function closest (needle, haystack, key=null) {
    if (key==null) {
        return haystack.reduce((a, b) => Math.abs(needle - b) < Math.abs(needle - a) ? b : a);
    }
    return haystack.reduce((a, b) => {
        if (b[key] == null) return a;
        if (a[key] == null) return b;
        return Math.abs(needle - b[key]) < Math.abs(needle - 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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接