【问题标题】:Remove duplicate objects from array by a certain merge algorithm通过某种合并算法从数组中删除重复对象
【发布时间】:2013-07-06 02:58:45
【问题描述】:

这是我的想法:

给定一个对象数组:

[
    {
        "name": "Kirk",
        "count": 1
    },
    {
        "name": "Spock",
        "count": 1
    },
    {
        "name": "Kirk",
        "count": 1
    }
]

我想得到:

[
    {
        "name": "Kirk",
        "count": 2
    },
    {
        "name": "Spock",
        "count": 1
    }
]

我想知道是否已经有一种算法,也许结合了一些高阶函数来实现这一点。 我可以使用循环轻松地做到这一点,但我正在寻找一种使用高阶函数来解决它的方法。如果有人能指出我应该用什么来实现这一点,那就太好了。同样,我正在寻找尽可能优雅的东西(两个地图和一个过滤器不会比循环有很大的改进)。

这是我目前的解决方案,我正在寻找更好的解决方案(更好是指更具表现力):

function mergeDuplicates(input) {
  var output = [];
  var existingItem = null;
  input.forEach(function (inputItem) {
    existingItem = _.find(output, function (outputItem) {
      return inputItem.name === outputItem.name;
    });
    existingItem ? existingItem.count += 1 : output.push({
      name: inputItem.name,
      count: 1
    });
    existingItem = null;
  });
  return output;
}

为了使第 10 行更清楚:在原始数组中,count 可能不存在或为 1,因此我将其设置为 1。

【问题讨论】:

  • 我很确定我以前在 StackOverflow 上看到过这个问题。 (我自己也不知道答案是什么)
  • 如果您不介意外部库。试试 underscore.js uniq 函数。 underscorejs.org/#uniq
  • @David,没关系,因为我怀疑答案是否符合我的标准。
  • 如果原始数组包含"count"大于1的对象怎么办?
  • @groovy 不会。其实一开始是没有count的。

标签: javascript algorithm merge higher-order-functions


【解决方案1】:

我认为最好的方法是散列每个不存在的对象,并删除您在结构中发现的已经散列的对象。这样,您将只检查每个对象的存在 1(取决于您的哈希方案)。

【讨论】:

    【解决方案2】:

    如果您想使用,只是一个功能。

    function merge(arr) {    
       for(var o = {}, i; i=arr.shift(); o[i.name] = i.count + (o[i.name] || 0));
       for(i in o) arr.push({name:i, count:o[i]});
    }
    

    来电:

    var myArray = [{"name":"Kirk","count":1},
                   {"name":"Spock","count":1},
                   {"name":"Kirk","count":1}];
    
    merge(myArray);   
    
    // myArray is now :  [{"name":"Kirk","count":2}, {"name":"Spock","count":1}]
    

    【讨论】:

    • 不是我所希望的,而是一个有趣的解决方案。
    • 非常巧妙的技巧,不适用于具有超过 2 个属性的对象。
    【解决方案3】:

    您可以使用reduce,这实际上是一个折叠。

    a.reduce(function(p, c) {
            var n = c.name;
            if (p[n])
                p[n].count++;
            else
                p[n] = c;
            return p;
        }, {})
    

    会给你一个以“Kirk”和“Spock”​​为键的对象,你想要的值。

    【讨论】:

    • 我实际上希望输出与我的示例中一样 - 以 namecount 作为键
    【解决方案4】:

    我知道这是一个老问题,但我忍不住尝试解决它。我们使用排序和归约,而不是两个映射和一个过滤器。这是一个有趣的整理:-)

    function mergeDuplicates(list, prop, cb){
      return list.sort(function(a,b){
        if(a[prop] < b[prop]){ return -1;}
        if(a[prop] > b[prop]){return 1;}
        return 0;
      }).reduce(function(acc, item, index, array){
        if(index > 0 && array[index-1][prop] === item[prop]){
          cb(acc[acc.length-1], item);
          return acc;
        }else{
          var newItem = Object.assign({}, item);
          cb(newItem);
          acc.push(newItem);
          return acc;
        }
      }, []);
    }
    

    然后像这样使用它:

    var newList = mergeDuplicates(list, "name", function(item, dup){
        if(dup){
          item.count++;
        }else{
          item.count = 1;
        }        
    });
    

    编辑:这是使用 reduce 并使用对象作为哈希图来存储重复项的另一种方法(类似于其他一些答案)。这个用ramdajs

    const mergeDups = (cb, prop, list) => R.pipe(
      R.reduce((acc, item) => (
        R.has(item[prop], acc) ?
          R.assoc(item[prop], cb(acc[item[prop]], item), acc) :
          R.assoc(item[prop], cb(item), acc)     
      ), {}), 
      R.values
    )(list);
    
    const cb = (i, d) => ( !R.isNil(d) ? 
      R.assoc('count', i.count + 1, i) :
      R.assoc('count', 1, i) )
    
    mergeDups(cb, 'name', items);
    

    Here it is in the repl on Ramda's site

    【讨论】:

      【解决方案5】:

      试试这个更好,我会有用的解决你的问题

      cleanup(arrayOfObj, 'name');
      
      function cleanup(arr, prop) {
        var new_arr = [];
        var lookup = {};
        for (var i in arr) {
         lookup[arr[i][prop]] = arr[i];
        }
        for (i in lookup) {
         new_arr.push(lookup[i]); 
        }
        return new_arr;
      }

      【讨论】:

        【解决方案6】:

        另一个使用reduce function的版本:

        var items =
        [
            {
                "name": "Kirk",
                "count": 1
            },
            {
                "name": "Spock",
                "count": 1
            },
            {
                "name": "Kirk",
                "count": 1
            }
        ];
            
        
        var filtered = items.reduce(function(prev, current,index){ 
            if(!(current.name in prev.keys)) {
               prev.keys[current.name] = index;
               prev.result.push(current);
           } 
           else{
               prev.result[prev.keys[current.name]].count += current.count; 
           }
           return prev;
        },{result: [], keys: []}).result;
        
            
        
        document.getElementById("output").innerHTML = JSON.stringify(filtered,null,2);
        &lt;pre id='output' /&gt;

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-07-26
          • 2019-11-15
          • 2020-07-15
          • 1970-01-01
          • 2022-11-17
          • 1970-01-01
          • 2021-01-23
          • 1970-01-01
          相关资源
          最近更新 更多