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

时间:2019-08-08 11:19:47

标签: javascript arrays object ecmascript-6

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

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' })。

您可以看到年龄值不是线性的。

11 个答案:

答案 0 :(得分:1)

以下是解决问题的完全抽象的方法:

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

答案 1 :(得分:1)

您可以找到所有数字之间的差,而最接近零的那个就是您的结果,要实现这一点,我将.reduce()Math.abs()一起使用

const data = [
  { age: 52 },
  { age: 53 },
  { age: 54 },
  { age: 60 },
  { age: 66 },
  { age: 72 },
  { age: 78 },
  { age: 84 }
];

const get_age = (data, to_find) => 
  data.reduce(({age}, {age:a}) =>
     Math.abs(to_find - a) < Math.abs(to_find - age) ? {age: a} : {age}
  );
  
console.log(get_age(data, 61)); // {age: 60}
console.log(get_age(data, 50)); // {age: 52}
console.log(get_age(data, -1)); // {age: 52}
console.log(get_age(data, 90)); // {age: 84}

对于更一般化的对象(具有除age以外的其他属性),可以在分解时使用其余语法...,然后将其重新散布到返回的对象中(再次使用...):

const data = [
  { age: 52, id: 1},
  { age: 53, id: 2},
  { age: 54, id: 3},
  { age: 60, id: 4},
  { age: 66, id: 5},
  { age: 72, id: 6},
  { age: 78, id: 7},
  { age: 84, id: 8}
];

const get_age = (data, to_find) => 
  data.reduce(({age, ...resto}, {age:a, ...rest}) =>
     Math.abs(to_find - a) < Math.abs(to_find - age) ? {age: a, ...rest} : {age, ...resto}
  );
  
console.log(get_age(data, 61)); // {age: 60, id: 4}
console.log(get_age(data, 50)); // {age: 52, id: 1}
console.log(get_age(data, -1)); // {age: 52, id: 1}
console.log(get_age(data, 90)); // {age: 84, id: 8}

答案 2 :(得分:0)

假设您的列表未排序,并且您不想对列表进行排序。因此,您可以选择第一个对象,遍历列表并检查是否有适合您需求的物品,而不是当前选择的物品。如果是这样,您只需用更好的产品代替。

例如

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.

如果对数据集进行排序,则可以优化运行时间。

答案 3 :(得分: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)

答案 4 :(得分: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)

答案 5 :(得分: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

答案 6 :(得分: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);

答案 7 :(得分: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>

答案 8 :(得分: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)

答案 9 :(得分:0)

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;

}

答案 10 :(得分: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));