通过某种合并算法从数组中删除重复的对象

时间:2013-07-08 17:15:19

标签: javascript algorithm merge higher-order-functions

以下是我的想法:

给定一组对象:

[
    {
        "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.

6 个答案:

答案 0 :(得分:2)

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

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

您可以使用实际上是折叠的reduce

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

会给你一个以“Kirk”和“Spock”​​为关键字的对象,你想要的是什么。

答案 3 :(得分:1)

我知道这是一个老问题,但我无法抗拒试图解决它。我们使用排序然后使用reduce来代替两个映射和过滤器。这很有趣: - )

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并使用对象作为hashmap来存储重复项(类似于其他一些答案)。这个使用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

答案 4 :(得分:0)

更好地尝试这一点我将有用解决您的问题

&#13;
&#13;
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;
}
&#13;
&#13;
&#13;

答案 5 :(得分:0)

使用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);
<pre id='output' />