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?


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


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

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" } ]

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

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

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


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]]


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 ] ]

3

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

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

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
}

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

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')


sua solução não funciona para matrizes de objeto, mas apenas para matrizes
Toucouleur
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.