从javascript中的对象数组中删除重复项
我有一个包含对象数组的对象。
things = new Object(); things.thing = new Array(); things.thing.push({place:"here",name:"stuff"}); things.thing.push({place:"there",name:"morestuff"}); things.thing.push({place:"there",name:"morestuff"});
我想知道什么是从数组中删除重复的对象的最佳方法。 举个例子,事情会变成…
{place:"here",name:"stuff"}, {place:"there",name:"morestuff"}
提前致谢
让我们看看……一个原始的将是:
var obj = {}; for ( var i=0, len=things.thing.length; i < len; i++ ) obj[things.thing[i]['place']] = things.thing[i]; things.thing = new Array(); for ( var key in obj ) things.thing.push(obj[key]);
好的,我认为应该这样做。 看看,特拉维斯。
编辑
编辑代码以正确引用place
(以前的id
)属性。
用一些es6
魔法怎么样?
things.thing = things.thing.filter((thing, index, self) => index === self.findIndex((t) => ( t.place === thing.place && t.name === thing.name ) )
参考url
对于前端来说,这可能有点早,因为大量的浏览器仍然不支持es6function
如果您可以使用Javascript库(例如下划线或lodash),则build议查看其库中的_.uniq
函数。 从lodash
:
_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])
基本上,你传入的数组是在这里是一个对象字面值,你传入的属性,你想删除原始数据数组中的重复,如下所示:
var data = [{'name': 'Amir', 'surname': 'Rahnama'}, {'name': 'Amir', 'surname': 'Stevens'}]; var non_duplidated_data = _.uniq(data, 'name');
更新 :Lodash现在也引入了.uniqBy
。
我有这个完全相同的要求,基于单个字段上的重复删除数组中的重复的对象。 我在这里find代码: Javascript:从对象数组中删除重复
所以在我的例子中,我删除了具有重复的licenseNumstring值的数组中的任何对象。
var arrayWithDuplicates = [ {"type":"LICENSE", "licenseNum": "12345", state:"NV"}, {"type":"LICENSE", "licenseNum": "A7846", state:"CA"}, {"type":"LICENSE", "licenseNum": "12345", state:"OR"}, {"type":"LICENSE", "licenseNum": "10849", state:"CA"}, {"type":"LICENSE", "licenseNum": "B7037", state:"WA"}, {"type":"LICENSE", "licenseNum": "12345", state:"NM"} ]; function removeDuplicates(originalArray, prop) { var newArray = []; var lookupObject = {}; for(var i in originalArray) { lookupObject[originalArray[i][prop]] = originalArray[i]; } for(i in lookupObject) { newArray.push(lookupObject[i]); } return newArray; } var uniqueArray = removeDuplicates(arrayWithDuplicates, "licenseNum"); console.log("uniqueArray is: " + JSON.stringify(uniqueArray));
结果:
uniqueArray是:
[{"type":"LICENSE","licenseNum":"10849","state":"CA"}, {"type":"LICENSE","licenseNum":"12345","state":"NM"}, {"type":"LICENSE","licenseNum":"A7846","state":"CA"}, {"type":"LICENSE","licenseNum":"B7037","state":"WA"}]
如果您可以等待删除重复,直到所有添加后,典型的方法是首先sorting数组,然后消除重复。 sorting避免了N * N方法扫描每个元素的数组,当你走过他们。
“消除重复”function通常称为唯一或uniq 。 一些现有的实现可以结合这两个步骤,例如原型的uniq
这个post有几个想法尝试(和一些避免:-)), 如果你的图书馆还没有一个 ! 我个人认为这是最直接的一个:
function unique(a){ a.sort(); for(var i = 1; i < a.length; ){ if(a[i-1] == a[i]){ a.splice(i, 1); } else { i++; } } return a; } // Provide your own comparison function unique(a, compareFunc){ a.sort( compareFunc ); for(var i = 1; i < a.length; ){ if( compareFunc(a[i-1], a[i]) === 0){ a.splice(i, 1); } else { i++; } } return a; }
如果只需要比较一个对象的一个字段,那么使用数组迭代方法可以实现另一个选项:
function uniq(a, param){ return a.filter(function(item, pos, array){ return array.map(function(mapItem){ return mapItem[param]; }).indexOf(item[param]) === pos; }) } uniq(things.thing, 'place');
更新
我现在正确地阅读了这个问题。 这是做这件事的通用方法:你传递一个函数来testing一个数组中的两个元素是否相等。 在这种情况下,它会比较两个正在比较的对象的name
和place
属性的值。
function arrayContains(arr, val, equals) { var i = arr.length; while (i--) { if ( equals(arr[i], val) ) { return true; } } return false; } function removeDuplicates(arr, equals) { var originalArr = arr.slice(0); var i, len, j, val; arr.length = 0; for (i = 0, len = originalArr.length; i < len; ++i) { val = originalArr[i]; if (!arrayContains(arr, val, equals)) { arr.push(val); } } } function thingsEqual(thing1, thing2) { return thing1.place === thing2.place && thing1.name === thing2.name; } removeDuplicates(things.thing, thingsEqual);
你也可以使用Map
:
const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());
全样本:
const things = new Object(); things.thing = new Array(); things.thing.push({place:"here",name:"stuff"}); things.thing.push({place:"there",name:"morestuff"}); things.thing.push({place:"there",name:"morestuff"}); const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values()); console.log(JSON.stringify(dedupThings, null, 4));
结果:
[ { "place": "here", "name": "stuff" }, { "place": "there", "name": "morestuff" } ]
一套class轮使用Set
var things = new Object(); things.thing = new Array(); things.thing.push({place:"here",name:"stuff"}); things.thing.push({place:"there",name:"morestuff"}); things.thing.push({place:"there",name:"morestuff"}); // assign things.thing to myData for brevity var myData = things.thing; things.thing = Array.from(new Set(myData.map(JSON.stringify))).map(JSON.parse); console.log(things.thing)
另一个select是创build一个自定义的indexOf函数,该函数比较每个对象所选属性的值,并将其包含在reduce函数中。
var uniq = redundant_array.reduce(function(a,b){ function indexOfProperty (a, b){ for (var i=0;i<a.length;i++){ if(a[i].property == b.property){ return i; } } return -1; } if (indexOfProperty(a,b) < 0 ) a.push(b); return a; },[]);
这是一个使用JavaScript的新filterfunction的解决scheme,非常简单。 假设你有一个这样的数组。
var duplicatesArray = ['AKASH','AKASH','NAVIN','HARISH','NAVIN','HARISH','AKASH','MANJULIKA','AKASH','TAPASWENI','MANJULIKA','HARISH','TAPASWENI','AKASH','MANISH','HARISH','TAPASWENI','MANJULIKA','MANISH'];
filter函数将允许您创build一个新的数组,使用数组中的每个元素一次的callback函数。 所以你可以像这样设置独特的数组。
var uniqueArray = duplicatesArray.filter(function(elem, pos) {return duplicatesArray.indexOf(elem) == pos;});
在这种情况下,您唯一的数组将运行重复数组中的所有值。 elemvariables表示数组中元素的值(mike,james,james,alex),位置是数组0(0,1,2,3 …)中的0索引位置,以及duplicatesArray。 indexOf(elem)值只是原始数组中该元素第一次出现的索引。 所以,因为元素“james”是重复的,所以当我们循环遍历duplicateatesArray中的所有元素并将它们推送到uniqueArray时,我们第一次碰到james,我们的“pos”值是1,而indexOf(elem)也是1,所以詹姆斯被推到独特的arrays。 第二次我们击中了James,我们的“pos”值是2,我们的indexOf(elem)仍然是1(因为它只find数组元素的第一个实例),所以重复不被推。 因此,我们唯一的数组只包含唯一的值。
这是上述function的演示。 点击这里查看上面的函数示例
var data = [{'name': 'Amir', 'surname': 'Rahnama'}, {'name': 'Amir', 'surname': 'Stevens'}]; var non_duplidated_data = _.uniqBy(data, 'name');
这里有另一种方法来查找重复的数量,并从数据对象中轻松地将其删除。 “dupsCount”是重复文件数量。 先sorting你的数据,然后删除。 它会给你最快的重复删除。
dataArray.sort(function (a, b) { var textA = a.name.toUpperCase(); var textB = b.name.toUpperCase(); return (textA < textB) ? -1 : (textA > textB) ? 1 : 0; }); for (var i = 0; i < dataArray.length - 1; ) { if (dataArray[i].name == dataArray[i + 1].name) { dupsCount++; dataArray.splice(i, 1); } else { i++; } }
如果您需要基于对象中多个属性的唯一数组,可以使用map和组合对象的属性来完成此操作。
var hash = array.map(function(element){ var string = '' for (var key in element){ string += element[key] } return string }) array = array.filter(function(element, index){ var string = '' for (var key in element){ string += element[key] } return hash.indexOf(string) == index })
通用的任何数组对象:
/** * Remove duplicated values without losing information */ const removeValues = (items, key) => { let tmp = {}; items.forEach(item => { tmp[item[key]] = (!tmp[item[key]]) ? item : Object.assign(tmp[item[key]], item); }); items = []; Object.keys(tmp).forEach(key => items.push(tmp[key])); return items; }
希望它可以帮助任何人。
另一种方法是使用reduce函数,并有一个新的数组作为累加器。 如果在累加器数组中已经有一个同名的thing
,那么不要在那里添加它。
let list = things.thing; list = list.reduce((accumulator, thing) => { if (!accumulator.filter((duplicate) => thing.name === duplicate.name)[0]) { accumulator.push(thing); } return accumulator; }, []); thing.things = list;
我添加了这个答案,因为我无法find与Internet Explorer 11兼容的可读es6解决scheme(我使用babel来处理箭头函数)。问题是IE11没有Map.values()
或Set.values()
没有polyfill。 出于同样的原因,我使用filter()[0]
来获取第一个元素,而不是find()
。
考虑lodash.uniqWith
var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }]; _.uniqWith(objects, _.isEqual); // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]