Eliminar objetos duplicados con subrayado para Javascript

124

Tengo este tipo de matriz:

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];

Me gustaría filtrarlo para tener:

var bar = [ { "a" : "1" }, { "b" : "2" }];

Intenté usar _.uniq, pero supongo que porque { "a" : "1" }no es igual a sí mismo, no funciona. ¿Hay alguna manera de proporcionar subrayado uniq con una función de anulación igual?

más-
fuente
Por favor publique su código también
Chetter Hummin
¿Las cosas como { "a" : "2" }existen? Si es así, ¿es el atributo o el valor lo que lo hace único?
Matt
Sí, tengo un atributo como clave, implementé el índice que alguien me mostró sobre otro tema, pero luego quise limpiar mi código usando algunas bibliotecas comunes
más el
1
Pleae cambio aceptó la respuesta.
Vadorequest

Respuestas:

232

.uniq / .unique acepta una devolución de llamada

var list = [{a:1,b:5},{a:1,c:5},{a:2},{a:3},{a:4},{a:3},{a:2}];

var uniqueList = _.uniq(list, function(item, key, a) { 
    return item.a;
});

// uniqueList = [Object {a=1, b=5}, Object {a=2}, Object {a=3}, Object {a=4}]

Notas:

  1. Valor de retorno de devolución de llamada utilizado para comparación
  2. Primer objeto de comparación con un valor de retorno único utilizado como único
  3. underscorejs.org no demuestra el uso de devolución de llamada
  4. lodash.com muestra el uso

Otro ejemplo: usar la devolución de llamada para extraer marcas de automóviles, colores de una lista

Shanimal
fuente
falseno se requiere para _.uniq(). También en lodash podrías haberlo escrito así _.uniq(a, 'a');, ya que arrancará la propiedad ade los objetos.
Larry Battle
La "taquigrafía de devolución de llamada '_.pluck'" solo funciona si pasa un valor para isSorted (p _.uniq(a, false, 'a'). Ej. ) Pellizqué github / bestiejs / lodash y dijeron que el problema se solucionó al límite. Entonces, si no está utilizando una función, asegúrese de tener la última. Esto puede no ser un problema para subrayar.
Shanimal
2
Iterator no suena como un buen nombre, es una función de hash que determinará la identidad de cada objeto
Juan Mendes
Editado para usar la devolución de llamada para ser más coherente con lodash docs :)
Shanimal
1
Su ejemplo en jsbin puede tener una actualización. (1) Hace: _ (autos) .uniq ('make'). Map ('make'). ValueOf () AND (2) Colores: _ (cars) .uniq ('color'). Map ('color' ).valor de(). Puedes madurar el color y hacer cierres. (Todo eso si actualiza de lodash usado)
Vitor Tyburski
38

Si está buscando eliminar duplicados basados ​​en una identificación, puede hacer algo como esto:

var res = [
  {id: 1, content: 'heeey'},
  {id: 2, content: 'woah'}, 
  {id: 1, content:'foo'},
  {id: 1, content: 'heeey'},
];
var uniques = _.map(_.groupBy(res,function(doc){
  return doc.id;
}),function(grouped){
  return grouped[0];
});

//uniques
//[{id: 1, content: 'heeey'},{id: 2, content: 'woah'}]
Petter
fuente
La respuesta aceptada no funciona cuando el identificador único es a Date. Sin embargo esto sí.
gunwin
17

Implementación de la respuesta de Shiplu.

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];

var x = _.uniq( _.collect( foo, function( x ){
    return JSON.stringify( x );
}));

console.log( x ); // returns [ { "a" : "1" }, { "b" : "2" } ]
Larry Battle
fuente
por cierto, ¿cómo obtuviste 4 votos a favor? Para obtener las propiedades de su resultado, tendría que revertir cada valor de matriz nuevamente en un objeto. Algo así como JSON.parse(x[0]).aporque x no es una matriz de objetos, es una matriz de cadenas. Además, si agrega valores b a los únicos e invierte el orden de a / b, su función ya no los considera únicos. (por ejemplo, "{\" a \ ": \" 1 \ ", \" b \ ": 2}"! = "{\" b \ ": 2, \" a \ ": \" 1 \ "} ") Tal vez me estoy perdiendo algo, pero ¿no debería ser útil al menos el resultado? Aquí hay un jsbin para ilustrar jsbin.com/utoruz/2/edit
Shanimal
1
Tiene razón la condición de tener las mismas claves pero en un orden diferente interrumpe la implementación. Pero no estoy seguro de por qué solo está verificando la clave apara cada objeto, cuando podría haber objetos duplicados que no contengan la clave a. Sin embargo, tendría sentido si afuera una identificación única.
Larry Battle
Cuando estaba respondiendo la pregunta, me pareció que el objetivo de la pregunta era anular (a ==(=) b when a = b = {a:1}). El punto de mi respuesta fue el iterador. Traté de responder sin preocuparme por el motivo, que podría ser cualquier cosa, ¿verdad? (por ejemplo, tal vez querían extraer una lista de marcas, colores de una lista de automóviles en un espectáculo. jsbin.com/evodub/2/edit ) ¡Salud!
Shanimal
También creo que nos ayuda a dar respuestas concisas cuando alguien que hace una pregunta proporciona un motivo. Esta es una carrera, así que prefiero ser el primero y aclarar si es necesario. Feliz dia de San Patricio.
Shanimal
Bueno, acabo de dar otro voto positivo porque esto respondió mi pregunta sobre la comparación de matrices anidadas. Solo estaba buscando cómo anular eliterator
nevi_me
15

Cuando tengo una identificación de atributo, esta es mi forma preferida en guión bajo:

var x = [{i:2}, {i:2, x:42}, {i:4}, {i:3}];
_.chain(x).indexBy("i").values().value();
// > [{i:2, x:42}, {i:4}, {i:3}]
esmoquin
fuente
12

El uso de guiones únicos de subrayado lib funciona para mí, estoy haciendo una lista única basada en _id y luego devolviendo el valor de cadena de _id:

var uniqueEntities = _.uniq(entities, function (item, key, a) {
                                    return item._id.toString();
                                });
Aqib Mumtaz
fuente
10

Aquí hay una solución simple, que utiliza una comparación profunda de objetos para verificar si hay duplicados (sin recurrir a la conversión a JSON, que es ineficiente y hacky)

var newArr = _.filter(oldArr, function (element, index) {
    // tests if the element has a duplicate in the rest of the array
    for(index += 1; index < oldArr.length; index += 1) {
        if (_.isEqual(element, oldArr[index])) {
            return false;
        }
    }
    return true;
});

Filtra todos los elementos si tienen un duplicado más adelante en la matriz, de modo que se mantenga el último elemento duplicado.

La prueba de un uso duplicado _.isEqualque realiza una comparación profunda optimizada entre los dos objetos, consulte la documentación de subrayado isEqual para obtener más información.

editar: actualizado para usar, _.filterque es un enfoque más limpio

Joshua Bambrick
fuente
¿No depende de tener una propiedad única predefinida? Me gusta.
Don McCurdy
1
Una buena solución para una pequeña matriz de objetos pero un bucle dentro de un bucle es costoso en comparación con proporcionar una identificación uniq.
Penner
7

Prueba la función iteradora

Por ejemplo, puede devolver el primer elemento

x = [['a',1],['b',2],['a',1]]

_.uniq(x,false,function(i){  

   return i[0]   //'a','b'

})

=> [['' a ', 1], [' b ', 2]]

IvanM
fuente
el argumento de segundos es realmente opcional, también puedes hacerlo_.uniq(x,function(i){ return i[0]; });
jakecraige
3

Aquí está mi solución (coffeescript):

_.mixin
  deepUniq: (coll) ->
    result = []
    remove_first_el_duplicates = (coll2) ->

      rest = _.rest(coll2)
      first = _.first(coll2)
      result.push first
      equalsFirst = (el) -> _.isEqual(el,first)

      newColl = _.reject rest, equalsFirst

      unless _.isEmpty newColl
        remove_first_el_duplicates newColl

    remove_first_el_duplicates(coll)
    result

ejemplo:

_.deepUniq([ {a:1,b:12}, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ],[ 2, 1, 2, 1 ], {a:1,b:12} ]) 
//=> [ { a: 1, b: 12 }, [ 2, 1, 2, 1 ], [ 1, 2, 1, 2 ] ]
szymanowski
fuente
3

con guión bajo tuve que usar String () en la función iteratee

function isUniq(item) {
    return String(item.user);
}
var myUniqArray = _.uniq(myArray, isUniq);
dam1
fuente
0

Quería resolver esta solución simple de una manera sencilla de escribir, con un poco de molestia en los gastos computacionales ... pero ¿no es una solución trivial con una definición mínima variable, verdad?

function uniq(ArrayObjects){
  var out = []
  ArrayObjects.map(obj => {
    if(_.every(out, outobj => !_.isEqual(obj, outobj))) out.push(obj)
  })
  return out
}
Junji Shimagaki
fuente
0
var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) { 
        return JSON.stringify(f); 
    }), function (gr) { 
        return gr[0]; 
    }
);

Vamos a romper esto. Primero, agrupemos los elementos de la matriz por su valor en cadena

var grouped = _.groupBy(foo, function (f) { 
    return JSON.stringify(f); 
});

grouped parece:

{
    '{ "a" : "1" }' = [ { "a" : "1" } { "a" : "1" } ],
    '{ "b" : "2" }' = [ { "b" : "2" } ]
}

Luego tomemos el primer elemento de cada grupo

var bar = _.map(grouped, function(gr)
    return gr[0]; 
});

bar parece: [ { "a" : "1" }, { "b" : "2" } ]

Ponlo todo junto:

var foo = [ { "a" : "1" }, { "b" : "2" }, { "a" : "1" } ];
var bar = _.map(_.groupBy(foo, function (f) { 
        return JSON.stringify(f); 
    }), function (gr) { 
        return gr[0]; 
    }
);
Kelly Bigley
fuente
3
Bienvenido a stackoverflow. Además del código que ha proporcionado, intente dar alguna explicación sobre por qué y cómo soluciona el problema.
jtate
buena llamada. Gracias. Actualizado con un desglose de cómo funciona esto.
Kelly Bigley
-5

Puedes hacerlo en forma abreviada como:

_.uniq(foo, 'a')

nnattawat
fuente
su solución no funciona para matrices de objetos, sino solo para matrices
Toucouleur