【问题标题】:What's an effective way to merge arrays based on the nth element?什么是基于第 n 个元素合并数组的有效方法?
【发布时间】:2018-06-24 15:34:39
【问题描述】:

这相对简单,但让我烦恼的是我找不到更优雅的解决方案。

假设您有一个 n 维数组的数组,并且您想通过某种方法(比如加法)将具有匹配第 m 个元素的数组压缩在一起。

示例:[[1, 100], [1, 200], [2, 100], [2, 105], [3, 202], [4, 133]]

结果: [[1, 300], [2, 205], [3, 202], [4, 133]]

我想出的解决方案对我来说似乎真的不优雅。有没有办法用更少的代码或更有效的方式来做到这一点?

function mergePairs(originalArray) {
  const newArray = [];
  originalArray.forEach((pair) => {
    const foundPair = newArray.find((newPair) => {
      return newPair[0] === pair[0];
    });
    if (foundPair) {
      foundPair[1] += pair[1];
    } else {
      newArray.push(pair);
    }
  });
  return newArray;
}

顺便说一句:如果 lodash 中有一个厚脸皮的单线人以一种超级低效的方式完成同样的事情,我也很高兴。

【问题讨论】:

  • 如果您搜索“基于属性合并对象”,您将获得比查找数组对象的特殊情况更多的结果。
  • 考虑到你的代码已经达到了预期的效果,这个问题可能更适合StackExchange's CodeReview 代替:)
  • m 是如何确定的?是不是一开始就给定的常数?

标签: javascript arrays algorithm optimization


【解决方案1】:

如果您只对数字进行排序,并且您希望数组已经按升序排序(如您的示例):

function mergePairs(arr) {
  return arr.sort((a, b) => {
    if (a[0] === b[0]){
      a[1] += b[1];
      b[0] = 'del'
    }
  }).filter(val => val[0] !== 'del');
}

或者您可以对其进行排序以确保安全:

function mergePairs(arr) {
  arr.sort((a, b) => a[0] === b[0]);
  return arr.sort((a, b) => {
    if (a[0] === b[0]){
      a[1] += b[1];
      b[0] = 'del'
    }
  }).filter(val => val[0] !== 'del');
}

【讨论】:

    【解决方案2】:

    就我个人而言,我发现将配对拆分然后重新组合更有意义。我使用 indexOf 匹配数组,因为它比整个原始数组列表更有效。每次遇到新值时,我都会将其添加到匹配列表中。匹配值可以是任何东西,而不仅仅是数字,并且不必避开任何关键字。 ~ 将 -1 从 not found 转换为 0 这是错误的。

    function combine (complexArray){
        var matches = [];
        var totals = [];
    
        complexArray.forEach(simplify);
    
        function simplify(item) {
            var match = matches.indexOf(item[0]);
            if(~match) {
                totals[match] += item[1];
            } else {
                matches.push(item[0]);
                totals.push(item[1]);
            }
        }
    
        return matches.map(function(value, index) {
            return [value, totals[index]];
        });
    }
    

    【讨论】:

      【解决方案3】:

      对于您的具体情况,您可以使用 reducemap

      function mergePairs (originalArray) {
        const o = originalArray.reduce((a, e) => (a[e[0]] = ~~a[e[0]] + e[1]) && a, {});
        return Object.keys(o).map(k => [+k, o[k]]);
      }
      
      console.log(mergePairs ([[1, 100], [1, 200], [2, 100], [2, 105], [3, 202], [4, 133]]))

      【讨论】:

      • Reduce 是个好主意,这应该比 OP 的方法快得多(仅O(n))。但是在回调返回中稍微冗长一些可能会很好——它不容易阅读。
      • @ChrisChudzicki 是什么让你认为reduceforEach 快?
      • @Bergi 我不确定reduceforEach 的相对速度是多少。但是 OP 的方法有一个嵌套循环:forEach 原始数组的元素,find 新数组的元素。对于具有 k 不同第一个键的长度 n 数组,我相信这将是 O(kn)。我相信这里的reduce方法只有O(n)
      • @ChrisChudzicki 是的,但这里的关键是在循环中使用对象作为查找映射,而不是嵌套循环。它与reduce 没有任何关系。
      • @Bergi 我同意!我最初的评论有两个不相关的陈述——它本来可以更好的措辞:)
      猜你喜欢
      • 2014-01-26
      • 2011-07-02
      • 2020-11-30
      • 2012-12-27
      • 1970-01-01
      • 1970-01-01
      • 2022-08-10
      • 2014-04-01
      相关资源
      最近更新 更多