【问题标题】:Merge and Dedupe Array of Complex Objects with Arrays用数组合并和去重复杂对象数组
【发布时间】:2021-03-11 09:00:46
【问题描述】:

我有一个非常复杂的问题,我似乎无法弄清楚。我有两个要合并分数的对象数组。它应该根据分数合并/附加某些属性。例如,在两个数组之间共有 4 个gameId,其中 3 个是唯一的。合并时,如果它与 gameId 相同,则应合并 _scores 部分,因此在这种情况下,它将是 EarthNormal 合并。但问题是有时_scores 中的score 可能有重复的分数,因此 BAR 和 BASH 看起来几乎完全相同但不同,它可以附加但 FOO 的分数在两者上完全相同,所以我不希望它合并到分数中(如果有意义的话)。

const arr1 = [{
  "gameId": "AirNormal",
  "_scores":
    [{
      "score": 144701,
      "playerName": "FOO",
      "fullCombo": true,
      "timestamp": 1599968866
    }]
}, {
  "gameId": "EarthNormal",
  "_scores":
    [{
      "score": 177352,
      "playerName": "BAR",
      "fullCombo": true,
      "timestamp": 1599969253
    }, {
      "score": 164665,
      "playerName": "FOO",
      "fullCombo": false,
      "timestamp": 1599970971
    }]
}];

const arr2 = [{
  "gameId": "EarthNormal",
  "_scores":
    [{
      "score": 177352,
      "playerName": "BASH",
      "fullCombo": false,
      "timestamp": 1512969017
    }, {
      "score": 164665,
      "playerName": "FOO",
      "fullCombo": false,
      "timestamp": 1599970971
    }]
}, {
  "gameId": "FireNormal",
  "_scores":
    [{
      "_score": 124701,
      "_playerName": "FOO",
      "_fullCombo": true,
      "_timestamp": 1591954866
    }]
}];

我希望最终的合并数组看起来像:

mergedArray = [{
  "gameId": "AirNormal",
  "_scores":
    [{
      "score": 144701,
      "playerName": "FOO",
      "fullCombo": true,
      "timestamp": 1599968866
    }]
}, {
  "gameId": "EarthNormal",
  "_scores":
    [{
      "score": 177352,
      "playerName": "BAR",
      "fullCombo": true,
      "timestamp": 1599969253
    }, {
      "score": 177352,
      "playerName": "BASH",
      "fullCombo": false,
      "timestamp": 1512969017
    }, {
      "score": 164665,
      "playerName": "FOO",
      "fullCombo": false,
      "timestamp": 1599970971
    }]
}, {
  "gameId": "FireNormal",
  "_scores":
    [{
      "score": 124701,
      "playerName": "FOO",
      "fullCombo": true,
      "timestamp": 1591954866
    }]
}]

我尝试过这样做并使用lodash

let merged = [...arr1, ...arr2];

merged = _.uniqBy[merged, 'gameId']
let scoresMerge = _.uniqBy[merged, '_scores']

console.log(scoresMerge);

但它并没有像我预期的那样工作。我是不是在错误地处理这个问题?

【问题讨论】:

    标签: javascript arrays ecmascript-6 lodash


    【解决方案1】:

    使用 vanilla javascript 相当简单。

    • 使用解构合并数组
    • reduce() 合并数组到一个由gameId 索引的对象中
    • 使用.some()对照累积的_scores数组检查每个_score对象的所有属性,如果没有找到匹配则推送。
    • 使用Object.values()返回缩小对象的值

    const arr1 = [{  "gameId": "AirNormal",  "_scores":    [{      "score": 144701,      "playerName": "FOO",      "fullCombo": true,      "timestamp": 1599968866    }]}, {  "gameId": "EarthNormal",  "_scores":    [{      "score": 177352,      "playerName": "BAR",      "fullCombo": true,      "timestamp": 1599969253    }, {      "score": 164665,      "playerName": "FOO",      "fullCombo": false,      "timestamp": 1599970971    }]}];
    
    const arr2 = [{"gameId": "EarthNormal","_scores":[{"score": 177352,"playerName": "BASH","fullCombo": false,"timestamp": 1512969017}, {"score": 164665,"playerName": "FOO","fullCombo": false,"timestamp": 1599970971}]}, {"gameId": "FireNormal","_scores":[{"_score": 124701,"_playerName": "FOO","_fullCombo": true,"_timestamp": 1591954866}]}];
    
    
    const merged = Object.values([...arr1, ...arr2].reduce((a, {gameId, _scores}) => {
      // retrieve gameId object otherwise initialize it.
      a[gameId] = {...a[gameId] ?? {gameId, _scores: []}};
      // iterate over all _score objects
      _scores.forEach(s => {
        // if accumulator _scores array doesn't have an object matching all properties, push _score 
        if (!a[gameId]['_scores'].some(o => {
            return !Object.entries(s).some(([k, v]) => o[k] !== v)})
          ) {
          a[gameId]['_scores'].push({...s});
        }
      });  
      return a;
    }, {}));
    
    console.log(merged);

    【讨论】:

      【解决方案2】:

      您需要识别具有相同gameId 的对象,然后对它们的_.scores 数组进行连接和重复数据删除。

      使用Array.reduce() 和 Map 很容易连接/删除非原始数组项。对于每个项目,您检查请求的键是否已经在 Map 中。如果不是,则将当前项目分配给 Map 的键。如果是,您将当前项目替换/合并到地图中的项目。

      完成 Map 的迭代后,使用 Array.from() 将 Map 的 .values() 迭代器转换为数组。

      const arr1 = [{"gameId":"AirNormal","_scores":[{"score":144701,"playerName":"FOO","fullCombo":true,"timestamp":1599968866}]},{"gameId":"EarthNormal","_scores":[{"score":177352,"playerName":"BAR","fullCombo":true,"timestamp":1599969253},{"score":164665,"playerName":"FOO","fullCombo":false,"timestamp":1599970971}]}];
      const arr2 = [{"gameId":"EarthNormal","_scores":[{"score":177352,"playerName":"BASH","fullCombo":false,"timestamp":1512969017},{"score":164665,"playerName":"FOO","fullCombo":false,"timestamp":1599970971}]},{"gameId":"FireNormal","_scores":[{"score":124701,"playerName":"FOO","fullCombo":true,"timestamp":1591954866}]}];
      
      const dedupLastBy = (a1 = [], a2 = [], key) => Array.from(
        [...a1, ...a2].reduce((acc, obj) => {
          const keyName = obj[key];
        
          if(acc.has(keyName)) acc.delete(keyName);
        
          return acc.set(keyName, obj);
        }, new Map()).values()
      )
      
      const handleDups = ({ _scores: a, ...o1 }, { _scores: b, ...o2 }) => ({
        ...o1,
        ...o2,
        _scores: dedupLastBy(a, b, 'playerName')
      });
      
      const result = Array.from([...arr1, ...arr2]
        .reduce((acc, o) => {
          const { gameId } = o;
          
          if(acc.has(gameId)) acc.set(gameId, handleDups(acc.get(gameId), o));
          else acc.set(gameId, o);
          
          return acc;
        }, new Map()).values());
      
      console.log(result);
      <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js" integrity="sha512-90vH1Z83AJY9DmlWa8WkjkV79yfS2n2Oxhsi2dZbIv0nC4E6m5AbH8Nh156kkM7JePmqD6tcZsfad1ueoaovww==" crossorigin="anonymous"></script>

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多