【问题标题】:Find object in array with closest value在数组中查找具有最接近值的对象
【发布时间】:2019-12-16 02:14:21
【问题描述】:

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

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

如您所见,年龄值不是线性的。

【问题讨论】:

  • 数组总是要排序的吗?
  • @nickzoum 是的,但是每个年龄值之间的差异并不总是相同的。
  • 输入 63 时最接近的值应该是多少? 66还是60?

标签: javascript arrays object ecmascript-6


【解决方案1】:

假设您的列表未排序,并且您不想对列表进行排序。所以你可以选择第一个对象,遍历你的列表并检查你是否得到了一个项目,它比你当前选择的项目更符合你的要求。如果是这样,您只需将您的项目替换为更好的项目。

例如

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.

如果您的数据集已排序,您可以优化您的运行时。

【讨论】:

    【解决方案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));

    【讨论】:

      【解决方案3】:

      您可以按差异对数组进行排序以查找年龄:

      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】:

        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】:

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

          当有 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

          【讨论】:

            【解决方案6】:

            您可以找到所有数字之间的差异,最接近零的数字将是您的结果,为此我使用了 .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 之外的其他属性的更通用的对象。

            【讨论】:

            • 如果有 2 个最接近的值说 (52,54,60) 并且输入是 53 会发生什么
            • @sumit 它将采用找到的第一个最接近的(在这种情况下为 52)
            • 假设数据对象在age 旁边有更多字段,我想获得完整的匹配对象...我需要为此更改什么?
            • @user3142695 如果您查看第二个示例代码 sn-p(默认隐藏),它使用 ...rest(这是一种用于在解构时获取所有其他属性的语法)。这将允许您保留其他属性
            【解决方案7】:

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

            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?
            • 我用60 选择较小的那个。可以通过删除条件中的等号来更改此行为,然后 66 是返回值。
            • 我认为我们需要考虑两者,不确定 op 想要什么,他的问题不清楚
            【解决方案8】:

            我制作了一个 lil' sn-p 代码来向您展示我将如何执行此操作。这将创建在任何对象数组上使用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>

            【讨论】:

              【解决方案9】:

              您可以找到具有最小值的最接近数组项,如下所示;

              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)
              

              【讨论】:

                【解决方案10】:
                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;
                
                }
                

                【讨论】:

                  【解决方案11】:

                  这是解决柯里化问题的一种实用方法:

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

                  【讨论】:

                    【解决方案12】:

                    @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

                    【讨论】:

                      猜你喜欢
                      • 2022-08-11
                      • 2019-11-02
                      • 1970-01-01
                      • 2021-12-06
                      • 2014-05-07
                      • 2021-12-26
                      • 1970-01-01
                      • 2012-10-27
                      • 1970-01-01
                      相关资源
                      最近更新 更多