Eliminar duplicados de una matriz de objetos en JavaScript

374

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. Entonces, por ejemplo, las cosas se volverían ...

{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}
Travis
fuente
¿Quiere decir cómo detiene una tabla hash / objeto con los mismos parámetros que se agregan a una matriz?
Matthew Lock
1
Mathew -> Si es más simple evitar que se agregue un objeto duplicado a la matriz en primer lugar, en lugar de filtrarlo más tarde, sí, eso también estaría bien.
Travis
2
Sigue sorprendiéndome cómo la gente nombra sus variables. A veces creo que realmente quieren que sea innecesariamente complicado. Siguiente para ver voluntad aaaaa.aaaa.push(...):)
dazito

Respuestas:

154

Un método primitivo 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]);
aefxx
fuente
15
Nunca debe usar la longitud en el ciclo for, porque ralentizará todo al calcularlo en cada iteración. Asignarlo a una variable fuera del bucle y pasar la variable en lugar de las cosas. Longitud.
0v3rth3d4wn
12
@aefxx No entiendo muy bien esta función, ¿cómo maneja la situación de que el "lugar" es el mismo pero el nombre es diferente, ¿debería considerarse dup o no?
Kuan
2
Aunque esto funciona, no se ocupa de una matriz ordenada ya que recuperar claves nunca está garantizado por el orden. Entonces, terminas clasificándolo de nuevo. Ahora, suponga que la matriz no está ordenada pero que su orden es importante, no hay forma de asegurarse de que la orden permanezca intacta
Deepak GM
1
@DeepakGM Tienes toda la razón. La respuesta no (necesariamente) preservará un orden dado. Si ese es un requisito, uno debe buscar otra solución.
aefxx
¿Cómo podría modificar lo anterior para eliminar objetos de una matriz que contiene X, así como deduplicado?
Ryan Holton
435

¿Qué tal con algo de es6magia?

things.thing = things.thing.filter((thing, index, self) =>
  index === self.findIndex((t) => (
    t.place === thing.place && t.name === thing.name
  ))
)

URL de referencia

Una solución más genérica sería:

const uniqueArray = things.thing.filter((thing, index) => {
  const _thing = JSON.stringify(thing);
  return index === things.thing.findIndex(obj => {
    return JSON.stringify(obj) === _thing;
  });
});

Ejemplo de Stackblitz

Eydrian
fuente
81
Esto se puede acortar a:things.thing = things.thing.filter((thing, index, self) => self.findIndex(t => t.place === thing.place && t.name === thing.name) === index)
Josh Cole
¡Funciona perfectamente! var uniqueArrayOfObjects = arrayOfObjects.filter (function (obj, index, self) {return index === self.findIndex (function (t) {return t ['obj-property'] === obj ['obj-property'] });}); Asegúrese de usar la sintaxis JS adecuada.
Mohamed Salem Lamiri
Esas son las sintaxis correctas de JS. La suya no utiliza 1) funciones de flecha gruesa 2) retorno implícito o 3) notación de puntos. La suya es la sintaxis de ES5. Los otros son principalmente ES6 (ECMA2015). Todos son válidos en 2017. Ver el comentario de jaredwilli.
agm1984
8
@vsync solo toma la respuesta de @ BKM y la arma, una solución genérica sería: const uniqueArray = arrayOfObjects.filter((object,index) => index === arrayOfObjects.findIndex(obj => JSON.stringify(obj) === JSON.stringify(object))); jsfiddle.net/x9ku0p7L/28
Eydrian
99
La clave aquí es que el método findIndex () devuelve el índice del primer elemento, por lo que si hay un segundo elemento que coincida, nunca se encontrará y se agregará durante el filtro. Lo estuve mirando por un minuto :)
JBaczuk
111

Si puede usar bibliotecas de Javascript, como subrayado o lodash, le recomiendo echar un vistazo a la _.uniqfunción en sus bibliotecas. De lodash:

_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])

Básicamente, pasa la matriz que aquí es un objeto literal y pasa el atributo con el que desea eliminar los duplicados en la matriz de datos original, de esta manera:

var data = [{'name': 'Amir', 'surname': 'Rahnama'}, {'name': 'Amir', 'surname': 'Stevens'}];
var non_duplidated_data = _.uniq(data, 'name'); 

ACTUALIZACIÓN : Lodash ahora ha introducido un .uniqBytambién.

ambodi
fuente
3
@Praveen Pds: ¿Dije algo sobre el guión bajo en el ejemplo de código? Dije 'lodash' tiene esta función y el subrayado tiene otras similares. Antes de votar, lea atentamente las respuestas.
ambodi
// Enumera objetos únicos utilizando _underscore.js holdingObject = _.uniq (holdingObject, function (item, key, name) {return item.name;});
praveenpds
26
Nota: ahora debe usar en uniqBylugar de uniq, por ejemplo, _.uniqBy(data, 'name')documentación: lodash.com/docs#uniqBy
drmrbrewer
83

Tenía exactamente el mismo requisito, para eliminar objetos duplicados en una matriz, basados ​​en 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 tenga un valor de cadena duplicado 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"}]
James Drinkard
fuente
1
Esto sería más útil si la función también pudiera filtrar los objetos 'falsos'. for(var i in array) { if(array[i][prop]){ //valid lookupObject[array[i][prop]] = array[i]; } else { console.log('falsy object'); } }
Abdul Sadik Yalcin
¿Por qué no reducir la complejidad 0 (n) usando: for (let i in originalArray) { if (lookupObject[originalArray[i]['id']] === undefined) { newArray.push(originalArray[i]); } lookupObject[originalArray[i]['id']] = originalArray[i]; }
Tudor B.
esta es la mejor manera porque es importante saber qué es lo que desea que no se duplique. ¿Ahora se puede hacer esto a través del reductor para los estándares e6?
Christian Matthew
70

Los revestimientos más cortos para ES6 +

Encuentra los únicos iden una matriz.

arr.filter((v,i,a)=>a.findIndex(t=>(t.id === v.id))===i)

Único por múltiples propiedades ( placey name)

arr.filter((v,i,a)=>a.findIndex(t=>(t.place === v.place && t.name===v.name))===i)

Único por todas las propiedades (Esto será lento para matrices grandes)

arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i)

Mantener la última ocurrencia.

arr.slice().reverse().filter((v,i,a)=>a.findIndex(t=>(t.id === v.id))===i).reverse()
pollos
fuente
2
Magia, esta es la verdadera respuesta
Luis Contreras
48

Un revestimiento 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:

  1. new Set(myData.map(JSON.stringify))crea un objeto Set utilizando los elementos myData en cadena.
  2. Establecer objeto asegurará que cada elemento sea único.
  3. Luego creo una matriz basada en los elementos del conjunto creado usando Array.from.
  4. Finalmente, uso JSON.parse para convertir el elemento stringified nuevamente en un objeto.
Mμ.
fuente
18
el problema es que {a: 1, b: 2} no será igual a {b: 2, a: 1}
PirateApp
2
tenga en cuenta que habría problemas con las propiedades de fecha
MarkosyanArtur
Esta línea crea valores nulos aleatorios con un objeto de fila que no existe en la matriz original de objetos. ¿Puedes por favor ayudarme?
B1K
47

Usando ES6 + en una sola línea, puede obtener una lista única de objetos por clave:

const unique = [...new Map(arr.map(item => [item[key], item])).values()]

Se puede poner en una función:

function getUniqueListBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()]
}

Aquí hay un ejemplo de trabajo:

const arr = [
    {place: "here",  name: "x", other: "other stuff1" },
    {place: "there", name: "x", other: "other stuff2" },
    {place: "here",  name: "y", other: "other stuff4" },
    {place: "here",  name: "z", other: "other stuff5" }
]

function getUniqueListBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()]
}

const arr1 = getUniqueListBy(arr, 'place')

console.log("Unique by place")
console.log(JSON.stringify(arr1))

console.log("\nUnique by name")
const arr2 = getUniqueListBy(arr, 'name')

console.log(JSON.stringify(arr2))

Como funciona

Primero, la matriz se reasigna de manera que se pueda usar como entrada para un Mapa.

arr.map (item => [item [clave], item]);

lo que significa que cada elemento de la matriz se transformará en otra matriz con 2 elementos; la clave seleccionada como primer elemento y todo el elemento inicial como segundo elemento, esto se llama una entrada (por ejemplo , entradas de matriz , entradas de mapa ). Y aquí está el documento oficial con un ejemplo que muestra cómo agregar entradas de matriz en Map constructor.

Ejemplo cuando la clave es lugar :

[["here", {place: "here",  name: "x", other: "other stuff1" }], ...]

En segundo lugar, pasamos esta matriz modificada al constructor del Mapa y aquí está sucediendo la magia. Map eliminará los valores duplicados de las claves, manteniendo solo el último valor insertado de la misma clave. Nota : El mapa mantiene el orden de inserción. ( marque la diferencia entre Mapa y objeto )

Nuevo mapa (matriz de entrada recién mapeada arriba)

En tercer lugar, usamos los valores del mapa para recuperar los elementos originales, pero esta vez sin duplicados.

nuevo Map (mappedArr) .values ​​()

Y el último es agregar esos valores en una nueva matriz nueva para que pueda verse como la estructura inicial y devolver eso:

return [... new Map (mappedArr) .values ​​()]

V. Sambor
fuente
Esto no responde a la pregunta original, ya que se trata de una id. La pregunta necesita todo el objeto a ser únicos en todos los campos, tales como placeename
L. Países Bajos El
Su función ES6 parece muy concisa y práctica. ¿Puedes explicarlo un poco más? ¿Qué está pasando exactamente? ¿Se eliminan el primer o el último duplicado? ¿O es aleatorio, qué duplicado se elimina? Eso sería útil, gracias.
David Schumann
Por lo que puedo decir, se crea un mapa con el valor de la propiedad como clave. Pero no es 100% cómo o si se preserva el orden de la matriz.
David Schumann
1
Hola @DavidSchumann, actualizaré la respuesta y explicaré cómo funciona. Pero para una respuesta corta, el orden se conserva y el primero se elimina ... Solo piense en cómo se inserta en el mapa ... comprueba si la clave ya existe, la actualizará, por lo tanto, la última permanecerá
V Sambor
30

Aquí hay otra opción para hacerlo utilizando los métodos de iteración de Array si necesita comparar solo 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');
Alex Kobylinski
fuente
Aunque esto tiene un orden mayor que O (n²), esto se ajusta a mi caso de uso porque el tamaño de mi matriz siempre será inferior a 30. ¡Gracias!
Sterex
24

un trazador de líneas 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}),{})))

sravan ganji
fuente
1
Agradable y limpio si solo desea eliminar objetos con un solo valor duplicado, no tan limpio para objetos completamente duplicados.
David Barker,
22

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" generalmente se llama ú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 aún no tiene una ! Personalmente, creo 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;
    }
maccullt
fuente
Eso no funcionará para objetos genéricos sin un orden de clasificación natural.
Tim Down
Es cierto, agregué una versión de comparación proporcionada por el usuario.
maccullt
Su versión de comparación proporcionada por el usuario no funcionará porque si su función de comparación es function(_a,_b){return _a.a===_b.a && _a.b===_b.b;}entonces la matriz no se ordenará.
graham.reeds
1
Esa es una función de comparación no válida. De developer.mozilla.org/en/Core_JavaScript_1.5_Reference/… ... function compare (a, b) {if (a es menor que b según algún criterio de orden) return -1; si (a es mayor que b según el criterio de pedido) devuelve 1; // a debe ser igual a b return 0; } ...
maccullt
22

La forma más simple es usar filter:

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)

Alex dykyі
fuente
66
Es una buena práctica en Stack Overflow agregar una explicación de por qué su solución debería funcionar, especialmente cómo la suya es mejor que las otras respuestas. Para obtener más información, lea Cómo responder .
Samuel Liew
Esto no responde a la pregunta original, ya que se trata de una id. La pregunta necesita que todo el objeto sea único en todos los campos, tales como placeyname
L. Holanda
17

Esta es una forma genérica de hacer esto: pasa una función que prueba si dos elementos de una matriz se consideran iguales. En este caso, compara los valores de las propiedades namey placede los dos objetos que se comparan.

Respuesta ES5

function removeDuplicates(arr, equals) {
    var originalArr = arr.slice(0);
    var i, len, val;
    arr.length = 0;

    for (i = 0, len = originalArr.length; i < len; ++i) {
        val = originalArr[i];
        if (!arr.some(function(item) { return equals(item, val); })) {
            arr.push(val);
        }
    }
}

function thingsEqual(thing1, thing2) {
    return thing1.place === thing2.place
        && thing1.name === thing2.name;
}

var things = [
  {place:"here",name:"stuff"},
  {place:"there",name:"morestuff"},
  {place:"there",name:"morestuff"}
];

removeDuplicates(things, thingsEqual);
console.log(things);

Respuesta original de ES3

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);
Tim Down
fuente
1
Dos objetos no se evaluarán igual, incluso si comparten las mismas propiedades y valores.
kennebec
Sí, lo sé. Pero claro, no he podido leer la pregunta correctamente: no había descubierto que eran objetos con propiedades idénticas que necesitaba eliminar. Editaré mi respuesta.
Tim Down
1
en lugar de estar dentro de arrayContains- use Array.prototype..some method Devuelve verdadero si uno de los miembros de la matriz coincide con la condición
MarkosyanArtur
13

Para agregar uno más a la lista. Usando ES6 y Array.reducecon Array.find.
En este ejemplo, filtrar objetos basados ​​en una guidpropiedad.

let filtered = array.reduce((accumulator, current) => {
  if (! accumulator.find(({guid}) => guid === current.guid)) {
    accumulator.push(current);
  }
  return accumulator;
}, []);

Extendiendo este para permitir la selección de una propiedad y comprimirla en un solo revestimiento:

const uniqify = (array, key) => array.reduce((prev, curr) => prev.find(a => a[key] === curr[key]) ? prev : prev.push(curr) && prev, []);

Para usarlo, pase una matriz de objetos y el nombre de la clave que desea deducir como valor de cadena:

const result = uniqify(myArrayOfObjects, 'guid')
Pete B
fuente
11

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"
    }
]
Pragmateek
fuente
+1, bueno, explicar un poco más el funcionamiento interno de las deducciones sería bueno, por el lado positivo, ahora entiendo reducir: D
MimiEAM
11

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

Cliff Hall
fuente
Esto no responde a la pregunta original, ya que se trata de una id. La pregunta necesita que todo el objeto sea único en todos los campos como placeyname
L. Holanda
Este es un refinamiento de una generalización anterior del problema. La pregunta original fue publicada hace 9 años, por lo que el cartel original, probablemente no se preocupaba placey namehoy. Cualquiera que lea este hilo está buscando una manera óptima de deducir una lista de objetos, y esta es una forma compacta de hacerlo.
Cliff Hall
11

Una solución TypeScript

Esto eliminará los objetos duplicados y también preservará los tipos de los objetos.

function removeDuplicateObjects(array: any[]) {
  return [...new Set(array.map(s => JSON.stringify(s)))]
    .map(s => JSON.parse(s));
}

fuente
2
esto es genial y corto!
mojjj
Y súper lento también ...
L. Holanda
7

Considerando 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 }]
Justin
fuente
Perfecto ! Gracias ;-)
DonFabiolas
1
Ni el uniq ni el uniq de lodash hicieron el truco, pero su solución sí. ¡Gracias! Sin embargo, proporcione la fuente de su código si es una copia directa. lodash.com/docs/4.17.10#uniqWith
Manu CJ
5

Otra opción sería crear una función indexOf personalizada, que compare los valores de su propiedad elegida para cada objeto y la ajuste 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;
    },[]);
ZeroSum
fuente
esto funcionó muy bien para mí: lo lodash.isequalcombiné con el paquete npm como un comparador de objetos livianos para realizar un filtrado de matriz único ... por ejemplo, una matriz distinta de objetos. Acabo de cambiar en if (_.isEqual(a[i], b)) {lugar de buscar @ una sola propiedad
SliverNinja - MSFT
5

let myData = [{place:"here",name:"stuff"}, 
 {place:"there",name:"morestuff"},
 {place:"there",name:"morestuff"}];


let q = [...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];

console.log(q)

One-liner usando ES6 y new Map().

// assign things.thing to myData
let myData = things.thing;

[...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];

Detalles: -

  1. Al hacer .map()en la lista de datos y convertir cada objeto individual en una [key, value]matriz de pares (longitud = 2), el primer elemento (clave) sería la stringifiedversión del objeto y el segundo (valor) sería unobject sí mismo.
  2. Agregar la lista de matriz creada anteriormente new Map()tendría la clave como stringifiedobjeto y cualquier misma adición de clave resultaría en la anulación de la clave ya existente.
  3. El uso .values()daría MapIterator con todos los valores en un Mapa (obj en nuestro caso)
  4. Finalmente, spread ...operador para dar una nueva matriz con los valores del paso anterior.
Savan Akbari
fuente
4

Aquí hay una solución para es6 donde solo desea conservar el último elemento. Esta solución es funcional y cumple con el estilo 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' }]
Micah
fuente
Puede eliminar el duplicado y también puede eliminar todo el duplicado con este código. Niza
sg28
4

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 en 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, utilizamos la funcionalidad de cortocircuito de &&. Si el lado izquierdo de && se evalúa como verdadero, entonces 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 valor de obj.id (en este caso, en la primera iteración verificaría la propiedad '1'). Queremos lo contrario de lo que devuelve (ya sea verdadero o falso) por eso usamos el! in! uniq [obj.id]. Si uniq ya tiene la propiedad id, devuelve verdadero que se evalúa como falso (!) Indicando a la función de filtro que NO agregue ese obj. Sin embargo, si no encuentra la propiedad obj.id, devuelve falso, que luego se evalúa como verdadero (!) Y devuelve todo a la derecha de &&, o (uniq [obj.id] = verdadero). Este es un valor verdadero, que le dice al método de filtro que agregue ese obj a la matriz devuelta, y también agrega la propiedad {1: true} a uniq. Esto asegura que cualquier otra instancia de obj con esa misma identificación no se agregará nuevamente.

MarkN
fuente
¿Quizás explique su código y cómo responde la pregunta?
mix3d
Gracias mix3d. Agregué una aclaración.
MarkN
Gracias por explicar esto! Esta solución funciona para mí y es similar a un par de otros publicados aquí, aunque no entendí lo que estaba sucediendo :)
Tim Molloy
3
let data = [
  {
    'name': 'Amir',
    'surname': 'Rahnama'
  }, 
  {
    'name': 'Amir',
    'surname': 'Stevens'
  }
];
let non_duplicated_data = _.uniqBy(data, 'name');
qzttt
fuente
99
Agregue una explicación sobre su código para que los futuros visitantes puedan entender lo que está haciendo. Gracias.
Errores
Su respuesta depende de una biblioteca de códigos externa ...
Taylor A. Leach
3

Creo que una combinación de reducecon JSON.stringifya la perfección comparar objetos y selectivamente la adición de los que no están ya en el acumulador es una manera elegante.

Tenga en cuenta que JSON.stringifypodría convertirse en un problema de rendimiento en casos extremos donde la matriz tiene muchos Objetos y son complejos, PERO para la mayoría de las veces , esta es la forma más corta de ir en mi humilde opinión.

var collection= [{a:1},{a:2},{a:1},{a:3}]

var filtered = collection.reduce((filtered, item) => {
  if( !filtered.some(filteredItem => JSON.stringify(filteredItem) == JSON.stringify(item)) )
    filtered.push(item)
  return filtered
}, [])

console.log(filtered)

Otra forma de escribir lo mismo (pero menos eficiente):

collection.reduce((filtered, item) => 
  filtered.some(filteredItem => 
    JSON.stringify(filteredItem ) == JSON.stringify(item)) 
      ? filtered
      : [...filtered, item]
, [])
vsync
fuente
el que funciona para mi! ¡gracias!
javascript110899
2

Continuar explorando formas de ES6 de eliminar duplicados de una matriz de objetos: establecer el thisArgargumento de Array.prototype.filterto new Setproporciona 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 funciones de flecha () =>, ya que thisestá vinculado a su alcance léxico.

Leonid Pyrlia
fuente
2

es6 magia en una línea ... ¡legible en eso!

// returns the union of two arrays where duplicate objects with the same 'prop' are removed
const removeDuplicatesWith = (a, b, prop) => a.filter(x => !b.find(y => x[prop] === y[prop]);
Josiah Coad
fuente
2

Solución simple con los métodos auxiliares de matriz 'reduce' y 'find' de ES6

Funciona de manera eficiente y perfectamente bien!

"use strict";

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

// the logic is here

function removeDup(something) {
    return something.thing.reduce(function (prev, ele) {
        var found = prev.find(function (fele) {
            return ele.place === fele.place && ele.name === fele.name;
        });
        if (!found) {
            prev.push(ele);
        }
        return prev;
    }, []);
}
console.log(removeDup(things));
KJ Sudarshan
fuente
Esto me ayudó mucho, gracias
jpisty
1

Si no le importa que su matriz única se ordene 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²)).

Yelmo de Clemens
fuente
1

Esta es una manera simple 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))

se imprimirá en la consola:

[[object Object] {
name: "AAA"
}, [object Object] {
name: "BBB"
}]
Juraj
fuente
Esta solución está diseñada para eliminar la duplicidad de la matriz estática, pero cuando empuje los datos del backend a la matriz de datos, considere usar el reemplazo. Porque en este caso, el nuevo valor introducido en la matriz de datos se eliminará y el valor "antiguo" seguirá almacenado en la matriz de datos.
Juraj
1
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. De acuerdo con ese resultado, inserte el objeto en una nueva matriz obj La salida del código anterior es [{"item_id":1},{"item_id":2}]

Bibin Jaimon
fuente
Agregue alguna descripción
Mathews Sunny
@Billa ¿Está bien?
Bibin Jaimon
1

¿Has oído hablar de la biblioteca Lodash? Le recomiendo esta utilidad, cuando realmente no desea aplicar su lógica al código, y usar el código ya presente que es optimizado y confiable.

Considere 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 lodash. Aquí, puedes usar _.uniqBy

.uniqBy (matriz, [iteratee = .identity])

Este método es como _.uniq (que devuelve una versión libre de duplicados de una matriz, en la que solo se mantiene la primera aparición de cada elemento), excepto que acepta iteratee que se invoca para cada elemento de la matriz para generar el criterio por el cual Se calcula la unicidad.

Entonces, por ejemplo, si desea devolver una matriz que tenga un atributo único de 'lugar'

_.uniqBy (things.thing, 'place')

Del mismo modo, si desea un atributo único como 'nombre'

_.uniqBy (things.thing, 'name')

Espero que esto ayude.

¡Salud!

Mayank Gangwal
fuente