2

这是我的想法:

给定一个对象数组:

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

4

6 回答 6

2

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

于 2013-07-08T17:23:29.880 回答
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}]
于 2013-07-08T17:44:12.940 回答
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”​​为键的对象,你想要什么作为值。

于 2013-07-08T17:24:31.003 回答
1

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

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

这是 Ramda 网站上的 repl

于 2016-01-21T22:21:45.853 回答
0

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

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

于 2014-10-02T18:32:42.770 回答
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' />

于 2016-02-01T12:59:34.757 回答