repetidos - Eliminar duplicados de una matriz de objetos en JavaScript
eliminar elementos repetidos de un array (30)
Tengo un objeto que contiene una matriz de objetos.
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"});
Me pregunto cuál es el mejor método para eliminar objetos duplicados de una matriz. Así, por ejemplo, las cosas. Todo se convertiría en ...
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}
¿Has oído hablar de la biblioteca de Lodash? Le recomiendo esta utilidad, cuando realmente no quiera aplicar su lógica al código, y use el código ya presente que es optimizado y confiable.
Considera hacer una matriz como esta
things.thing.push({place:"utopia",name:"unicorn"});
things.thing.push({place:"jade_palace",name:"po"});
things.thing.push({place:"jade_palace",name:"tigress"});
things.thing.push({place:"utopia",name:"flying_reindeer"});
things.thing.push({place:"panda_village",name:"po"});
Tenga en cuenta que si desea mantener un atributo único, puede hacerlo utilizando la biblioteca de lodash. Aquí, puedes usar _.uniqBy
.uniqBy (array, [iteratee = .identity])
Este método es como _.uniq (que devuelve una versión libre de duplicados de una matriz, en la que solo se conserva la primera aparición de cada elemento), excepto que acepta la iteración que se invoca para cada elemento de la matriz para generar el criterio por el cual la singularidad es computada
Entonces, por ejemplo, si desea devolver una matriz con un atributo único de ''lugar''
_.uniqBy (things.thing, ''lugar'')
Del mismo modo, si quieres atributo único como ''nombre''
_.uniqBy (things.thing, ''nombre'')
Espero que esto ayude.
¡Aclamaciones!
¿Qué tal con un es6
magia es6
?
things.thing = things.thing.filter((thing, index, self) =>
index === self.findIndex((t) => (
t.place === thing.place && t.name === thing.name
))
)
Para los front-end, esto podría tardar un poco en implementarse, ya que muchos navegadores usados ββaún no admiten las características de es6
Aquí hay otra opción para hacerlo usando métodos de iteración de matrices si necesita comparación solo por un campo de un objeto:
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'');
Aquí hay otra técnica para encontrar el número de duplicados y eliminarlos fácilmente de su objeto de datos. "dupsCount" es el número de archivos duplicados. ordena tus datos primero y luego elimina. Te dará la eliminación de duplicación más rápida.
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++;
}
}
Aquí hay una solución para es6 donde solo desea conservar el último elemento. Esta solución es funcional y cumple con el estilo de Airbnb.
const things = {
thing: [
{ place: ''here'', name: ''stuff'' },
{ place: ''there'', name: ''morestuff1'' },
{ place: ''there'', name: ''morestuff2'' },
],
};
const removeDuplicates = (array, key) => {
return array.reduce((arr, item) => {
const removed = arr.filter(i => i[key] !== item[key]);
return [...removed, item];
}, []);
};
console.log(removeDuplicates(things.thing, ''place''));
// > [{ place: ''here'', name: ''stuff'' }, { place: ''there'', name: ''morestuff2'' }]
Aquí hay una solución que usa una nueva función de filtro de JavaScript que es bastante fácil. Digamos que tienes una matriz como esta.
var duplicatesArray = [''AKASH'',''AKASH'',''NAVIN'',''HARISH'',''NAVIN'',''HARISH'',''AKASH'',''MANJULIKA'',''AKASH'',''TAPASWENI'',''MANJULIKA'',''HARISH'',''TAPASWENI'',''AKASH'',''MANISH'',''HARISH'',''TAPASWENI'',''MANJULIKA'',''MANISH''];
La función de filtro le permitirá crear una nueva matriz, utilizando una función de devolución de llamada una vez para cada elemento de la matriz. Por lo que podría configurar la matriz única de esta manera.
var uniqueArray = duplicatesArray.filter(function(elem, pos) {return duplicatesArray.indexOf(elem) == pos;});
En este escenario, su matriz única se ejecutará a través de todos los valores en la matriz duplicada. La variable elem representa el valor del elemento en la matriz (mike, james, james, alex), la posición es su posición indexada en 0 en la matriz (0,1,2,3 ...) y duplicatesArray. El valor de indexOf (elem) es solo el índice de la primera aparición de ese elemento en la matriz original. Entonces, debido a que el elemento ''james'' está duplicado, cuando recorramos todos los elementos en duplicatesArray y los empujamos a uniqueArray, la primera vez que golpeamos a james, nuestro valor "pos" es 1, y nuestro indexOf (elem) también es 1, por lo que James es empujado a uniqueArray. La segunda vez que golpeamos a James, nuestro valor "pos" es 2, y nuestro indexOf (elem) sigue siendo 1 (porque solo encuentra la primera instancia de un elemento de matriz), por lo que no se empuja el duplicado. Por lo tanto, nuestro uniqueArray solo contiene valores únicos.
Aquí está la demostración de la función anterior. Haga clic aquí para ver el ejemplo de la función anterior.
Continuar explorando las formas de ES6 de eliminar duplicados de la matriz de objetos: establecer thisArg
argumento de thisArg
de Array.prototype.filter
en un new Set
proporciona una alternativa decente:
const things = [
{place:"here",name:"stuff"},
{place:"there",name:"morestuff"},
{place:"there",name:"morestuff"}
];
const filtered = things.filter(function({place, name}) {
const key =`${place}${name}`;
return !this.has(key) && this.add(key);
}, new Set);
console.log(filtered);
Sin embargo, no funcionará con las funciones de flecha () =>
, ya que está vinculado a su alcance léxico.
Dang, niños, aplastemos esto, ¿por qué no lo hacemos?
let uniqIds = {}, source = [{id:''a''},{id:''b''},{id:''c''},{id:''b''},{id:''a''},{id:''d''}];
let filtered = source.filter(obj => !uniqIds[obj.id] && (uniqIds[obj.id] = true));
console.log(filtered);
// EXPECTED: [{id:''a''},{id:''b''},{id:''c''},{id:''d''}];
Esta es una forma sencilla de cómo eliminar la duplicidad de la matriz de objetos.
Trabajo mucho con datos y esto es útil para mí.
const data = [{name: ''AAA''}, {name: ''AAA''}, {name: ''BBB''}, {name: ''AAA''}];
function removeDuplicity(datas){
return datas.filter((item, index,arr)=>{
const c = arr.map(item=> item.name);
return index === c.indexOf(item.name)
})
}
console.log(removeDuplicity(data))
imprimirá en la consola:
[[object Object] {
name: "AAA"
}, [object Object] {
name: "BBB"
}]
Genérico para cualquier conjunto de objetos:
/**
* 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;
}
Espero que pueda ayudar a cualquiera.
Otra forma sería utilizar la función de reducción y tener una nueva matriz para ser el acumulador. Si ya hay una thing
con el mismo nombre en la matriz del acumulador, entonces no la agregue allí.
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;
Estoy agregando esta respuesta, porque no pude encontrar una solución es6 agradable y legible (uso babel para manejar funciones de flecha) que sea compatible con Internet Explorer 11. El problema es que IE11 no tiene Map.values()
o Set.values()
sin polyfill. Por la misma razón, utilicé filter()[0]
para obtener el primer elemento en lugar de find()
.
Otra opción sería crear una función indexOf personalizada, que compare los valores de su propiedad elegida para cada objeto y la envuelva en una función de reducción.
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;
},[]);
Para añadir uno más a la lista. Usando ES6 y Array.reduce
con Array.find
.
En este ejemplo, filtrando objetos basados ββen una propiedad guid
.
let filtered = array.reduce((accumulator, current) => {
if (! accumulator.find(({guid}) => guid === current.guid)) {
accumulator.push(current);
}
return accumulator;
}, []);
Si necesita una matriz única basada en múltiples propiedades en el objeto, puede hacerlo con el mapa y combinando las propiedades del objeto.
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
})
Si no desea especificar una lista de propiedades:
function removeDuplicates(myArr) {
var props = Object.keys(myArr[0])
return myArr.filter((item, index, self) =>
index === self.findIndex((t) => (
props.every(prop => {
return t[prop] === item[prop]
})
))
)
}
¡OBS! No es compatible con IE11.
Si no te importa que tu matriz única se clasifique después, esta sería una solución eficiente:
things.thing
.sort(((a, b) => a.place < b.place)
.filter((current, index, array) =>
index === 0 || current.place !== array[index - 1].place)
De esta manera, solo tiene que comparar el elemento actual con el elemento anterior en la matriz. Ordenar una vez antes de filtrar ( O(n*log(n))
) es más barato que buscar un duplicado en toda la matriz para cada elemento de la matriz ( O(n²)
).
Si puede esperar para eliminar los duplicados hasta después de todas las adiciones, el enfoque típico es primero ordenar la matriz y luego eliminar los duplicados. La clasificación evita el enfoque N * N de escanear la matriz para cada elemento a medida que los recorre.
La función "eliminar duplicados" se suele llamar única o uniq . Algunas implementaciones existentes pueden combinar los dos pasos, por ejemplo, uniq de prototipo
Esta publicación tiene algunas ideas para probar (y algunas para evitar :-)) si su biblioteca no tiene una . Personalmente me parece que este es el más sencillo:
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;
}
Si puedes usar bibliotecas de Javascript como guiones bajos o logotipos, recomiendo echar un vistazo a la función _.uniq
en sus bibliotecas. De lodash
:
_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])
Básicamente, usted pasa la matriz que aquí es un objeto literal y pasa el atributo con el que desea eliminar duplicados en la matriz de datos original, como esto:
var data = [{''name'': ''Amir'', ''surname'': ''Rahnama''}, {''name'': ''Amir'', ''surname'': ''Stevens''}];
var non_duplidated_data = _.uniq(data, ''name'');
ACTUALIZACIÓN : Lodash ahora ha introducido un .uniqBy
también.
También puedes usar un Map
:
const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());
Muestra completa
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));
Resultado:
[
{
"place": "here",
"name": "stuff"
},
{
"place": "there",
"name": "morestuff"
}
]
Tenía exactamente el mismo requisito, eliminar objetos duplicados en una matriz, en base a duplicados en un solo campo. Encontré el código aquí: Javascript: Eliminar duplicados de la matriz de objetos
Entonces, en mi ejemplo, estoy eliminando cualquier objeto de la matriz que tiene un valor duplicado de cadena licenseNum.
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));
Los resultados:
uniqueArray es:
[{"type":"LICENSE","licenseNum":"10849","state":"CA"},
{"type":"LICENSE","licenseNum":"12345","state":"NM"},
{"type":"LICENSE","licenseNum":"A7846","state":"CA"},
{"type":"LICENSE","licenseNum":"B7037","state":"WA"}]
Teniendo en cuenta 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 }]
Un trazador de líneas usando 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)
Explicación:
-
new Set(myData.map(JSON.stringify))
crea un objeto Set utilizando los elementos myData de cadena. - Establecer objeto asegurará que cada elemento sea único.
- Luego creo una matriz basada en los elementos del conjunto creado usando Array.from.
- Finalmente, utilizo JSON.parse para convertir el elemento de cadena en un objeto.
Veamos ... una primitiva sería:
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]);
Ok, creo que debería hacer el truco. Compruébalo, Travis.
EDITAR
Editado el código para hacer referencia correctamente a la propiedad place
(anterior id
).
removeDuplicates () toma una matriz de objetos y devuelve una nueva matriz sin ningún objeto duplicado (basado en la propiedad id).
const allTests = [
{name: ''Test1'', id: ''1''},
{name: ''Test3'', id: ''3''},
{name: ''Test2'', id: ''2''},
{name: ''Test2'', id: ''2''},
{name: ''Test3'', id: ''3''}
];
function removeDuplicates(array) {
let uniq = {};
return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true))
}
removeDuplicates(allTests);
Gastos esperados:
[
{name: ''Test1'', id: ''1''},
{name: ''Test3'', id: ''3''},
{name: ''Test2'', id: ''2''}
];
Primero, establecemos el valor de la variable uniq a un objeto vacío.
A continuación, filtramos a través de la matriz de objetos. Filter crea una nueva matriz con todos los elementos que pasan la prueba implementada por la función proporcionada.
return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
Arriba, usamos la funcionalidad de cortocircuito de &&. Si el lado izquierdo de && se evalúa como verdadero, devuelve el valor a la derecha de &&. Si el lado izquierdo es falso, devuelve lo que está en el lado izquierdo de &&.
Para cada objeto (obj) verificamos uniq para una propiedad llamada el valor de obj.id (en este caso, en la primera iteración verificará la propiedad ''1''). Queremos lo contrario de lo que devuelve (ya sea verdadero o falso) por lo que usamos el! en! uniq [obj.id]. Si uniq ya tiene la propiedad id, devuelve true, que se evalúa como falso (!) Y le dice a la función de filtro que NO agregue ese obj. Sin embargo, si no encuentra la propiedad obj.id, devuelve false, que luego se evalúa como true (!) Y devuelve todo a la derecha de &&, o (uniq [obj.id] = true). Este es un valor verdadero, que indica al método de filtro que agregue ese objeto a la matriz devuelta, y también agrega la propiedad {1: verdadero} a uniq. Esto garantiza que cualquier otra instancia de obj con esa misma ID no se volverá a agregar.
una línea está aquí
let arr = [
{id:1,name:"sravan ganji"},
{id:2,name:"anu"},
{id:4,name:"mammu"},
{id:3,name:"sanju"},
{id:3,name:"ram"},
];
console.log(Object.values(arr.reduce((acc,cur)=>Object.assign(acc,{[cur.id]:cur}),{})))
ACTUALIZADO
Ahora he leído la pregunta correctamente. Esta es una forma genérica de hacer esto: se pasa una función que prueba si dos elementos de una matriz se consideran iguales. En este caso, compara los valores de las propiedades de name
y place
de los dos objetos que se comparan.
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);
var uniq = {}
var arr = [{"id":"1"},{"id":"1"},{"id":"2"}]
var arrFiltered = arr.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
console.log(''arrFiltered'', arrFiltered)
var testArray= [''a'',''b'',''c'',''d'',''e'',''b'',''c'',''d''];
function removeDuplicatesFromArray(arr){
var obj={};
var uniqueArr=[];
for(var i=0;i<arr.length;i++){
if(!obj.hasOwnProperty(arr[i])){
obj[arr[i]] = arr[i];
uniqueArr.push(arr[i]);
}
}
return uniqueArr;
}
var newArr = removeDuplicatesFromArray(testArray);
console.log(newArr);
Output:- [ ''a'', ''b'', ''c'', ''d'', ''e'' ]
let data = [
{
''name'': ''Amir'',
''surname'': ''Rahnama''
},
{
''name'': ''Amir'',
''surname'': ''Stevens''
}
];
let non_duplicated_data = _.uniqBy(data, ''name'');
str =[
{"item_id":1},
{"item_id":2},
{"item_id":2}
]
obj =[]
for (x in str){
if(check(str[x].item_id)){
obj.push(str[x])
}
}
function check(id){
flag=0
for (y in obj){
if(obj[y].item_id === id){
flag =1
}
}
if(flag ==0) return true
else return false
}
console.log(obj)
str es una matriz de objetos. Existen objetos que tienen el mismo valor (aquí un pequeño ejemplo, hay dos objetos que tienen el mismo item_id que 2). check (id) es una función que verifica si existe o no algún objeto que tenga el mismo item_id. si existe devuelve falso de lo contrario devuelve verdadero. Según ese resultado, empuje el objeto en una nueva matriz obj La salida del código anterior es [{"item_id":1},{"item_id":2}]