以下是我的想法:
给定一组对象:
[
{
"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.
答案 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);
答案 4 :(得分: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;
}
&#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' />