Removendo Objetos Duplicados com Sublinhado para Javascript

124

Eu tenho esse tipo de matriz:

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

Eu gostaria de filtrá-lo para ter:

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

Eu tentei usar _.uniq, mas acho que porque { "a" : "1" }não é igual a si mesmo, não funciona. Existe alguma maneira de fornecer ao sublinhado uniq uma função igual substituída?

mais-
fonte
Por favor, poste seu código também
Chetter Hummin
Coisas como { "a" : "2" }existem? Em caso afirmativo, é esse o atributo ou o valor que o torna único?
Matt
Sim eu tenho um atributo como chave, implementei o índice alguém me mostrou em outro tópico, mas então eu queria limpar o meu código usando algumas bibliotecas comuns
mais-
1
Por favor, mude a resposta aceita.
Vadorequest

Respostas:

232

.uniq / .unique aceita um retorno de chamada

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 retorno de chamada usado para comparação
  2. Primeiro objeto de comparação com valor de retorno exclusivo usado como único
  3. underscorejs.org não demonstra uso de retorno de chamada
  4. lodash.com mostra o uso

Outro exemplo: usando o retorno de chamada para extrair marcas de carros, cores de uma lista

Shanimal
fonte
falsenão é necessário para _.uniq(). Também no lodash, você poderia ter escrito assim _.uniq(a, 'a');, pois ele arranca a propriedade ados objetos.
Larry Battle
O atalho "'_.pluck' de retorno de chamada" só funciona se você passar um valor para isSorted (por exemplo _.uniq(a, false, 'a')) Eu executei ping no github / bestiejs / lodash e eles disseram que o problema foi corrigido. Portanto, se você não estiver usando uma função, verifique se possui as últimas. Isso pode não ser um problema para o sublinhado.
Shanimal 16/03/2013
2
Iterator não soa como um nome bom, é um hash como função que será determinar a identidade de cada objeto
Juan Mendes
Editado para uso de retorno de chamada para ser mais consistente com docs lodash :)
Shanimal
1
Seu exemplo no jsbin pode ter uma atualização. (1) Cria: _ (cars) .uniq ('make'). Map ('make'). ValueOf () AND (2) Cores: _ (cars) .uniq ('color'). Map ('color' ).valor de(). Você pode amadurecer a cor e fazer fechamentos. (Tudo o que se fizer o upgrade de lodash usado)
Vitor Tyburski
38

Se você deseja remover duplicatas com base em um ID, pode fazer algo assim:

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
fonte
A resposta aceita não funciona quando o identificador exclusivo é a Date. No entanto, isso faz.
gunwin
17

Implementação da resposta da 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
fonte
btw, como você conseguiu 4 votos positivos? Para obter as propriedades do seu resultado, você teria que reverter cada valor de matriz novamente em um objeto. Algo como JSON.parse(x[0]).aporque x não é uma matriz de objetos, é uma matriz de seqüências de caracteres. Além disso, se você adicionar valores b aos únicos e inverter a ordem de a / b, eles não serão mais considerados exclusivos por sua função. (por exemplo, "" {\ "a \": \ "1 \", \ "b \": 2} "! =" {\ "b \": 2, \ "a \": \ "1 \"} ") Talvez esteja faltando alguma coisa, mas o resultado não deveria ser pelo menos útil? Aqui está um jsbin para ilustrar jsbin.com/utoruz/2/edit #
Shanimal 16/13
1
Você está certo na condição de ter as mesmas chaves, mas em ordem diferente interrompe a implementação. Mas não sei por que você está apenas checando a chave ade cada objeto, quando pode haver objetos duplicados que não contêm a chave a. No entanto, faria sentido se afosse um ID exclusivo.
Larry Battle
Quando eu estava respondendo à pergunta, pareceu-me que o foco da pergunta era substituir (a ==(=) b when a = b = {a:1}). O ponto da minha resposta foi o iterador. Tentei responder sem me preocupar com o motivo, o que poderia ser qualquer coisa, certo? (por exemplo, talvez eles quisessem extrair uma lista de marcas e cores de uma lista de carros em um show. jsbin.com/evodub/2/edit ) Felicidades!
Shanimal 17/03/2013
Também acho que isso nos ajuda a dar respostas concisas quando alguém que faz uma pergunta fornece motivos. Como é uma corrida, prefiro ser o primeiro e esclarecer se necessário. Feliz dia de St.Patrick.
Shanimal 17/03/2013
Bem, eu apenas dei outro voto positivo porque isso respondeu à minha pergunta sobre a comparação de matrizes aninhadas. Só estava procurando como substituir oiterator
nevi_me 12/04
15

Quando tenho um ID de atributo, esta é a minha maneira preferida em sublinhado:

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}]
tuxbear
fonte
12

Usando sublinhado lib exclusivo a seguir está funcionando para mim, eu estou tornando a lista exclusiva com base em _id e retornando o valor String de _id:

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

Aqui está uma solução simples, que usa uma comparação profunda de objetos para verificar se há duplicatas (sem recorrer à conversão para JSON, que é ineficiente e 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;
});

Ele filtra todos os elementos se eles tiverem uma duplicata posteriormente na matriz - de modo que o último elemento duplicado seja mantido.

Os testes para usos duplicados _.isEqualque executam uma comparação profunda otimizada entre os dois objetos, consulte a documentação isEqual de sublinhado para obter mais informações.

edit: atualizado para usar, _.filterque é uma abordagem mais limpa

Joshua Bambrick
fonte
Não depende de ter uma propriedade exclusiva predefinida? Eu gosto disso.
Don McCurdy
1
Uma boa solução para uma pequena matriz de objetos, mas um loop dentro de um loop é caro comparado ao fornecimento de um ID uniq.
Penner
7

Experimente a função do iterador

Por exemplo, você pode retornar o primeiro elemento

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

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

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

})

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

IvanM
fonte
o argumento segundos é realmente opcional, você também pode fazer_.uniq(x,function(i){ return i[0]; });
jakecraige
3

aqui está a minha solução (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

exemplo:

_.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
fonte
3

com sublinhado, tive que usar String () na função iteratee

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

Eu queria resolver essa solução simples de maneira direta, com um pouco de esforço computacional ... mas não é uma solução trivial com uma definição mínima de variável, é?

function uniq(ArrayObjects){
  var out = []
  ArrayObjects.map(obj => {
    if(_.every(out, outobj => !_.isEqual(obj, outobj))) out.push(obj)
  })
  return out
}
Junji Shimagaki
fonte
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 quebrar isso. Primeiro, vamos agrupar os itens da matriz pelo seu valor estrito

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

grouped parece:

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

Então vamos pegar o primeiro elemento de cada grupo

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

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

Coloque tudo 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
fonte
3
Bem-vindo ao stackoverflow. Além do código que você forneceu, tente dar uma explicação sobre por que e como isso corrige o problema.
jtate
boa decisão. Obrigado. Atualizado com uma análise detalhada de como isso funciona.
22619 Kelly Bigley
-5

Você pode fazer isso de forma abreviada como:

_.uniq(foo, 'a')

nnattawat
fonte
sua solução não funciona para matrizes de objeto, mas apenas para matrizes
Toucouleur