Obter todos os valores exclusivos em uma matriz JavaScript (remover duplicatas)

Eu tenho uma matriz de números que eu preciso ter certeza de que são únicos. Eu encontrei o trecho de código abaixo na internet e ele funciona muito bem até que o array tenha um zero nele. Eu encontrei este outro script aqui no SO que parece quase exatamente como ele, mas não falha.

Então, para ajudar a aprender, alguém pode me ajudar a determinar onde o script do protótipo está errado?

Array.prototype.getUnique = function() { var o = {}, a = [], i, e; for (i = 0; e = this[i]; i++) {o[e] = 1}; for (e in o) {a.push (e)}; return a; } 

Mais respostas da pergunta duplicada:

  • Remover duplicatas da matriz JavaScript

Pergunta semelhante:

  • Obter todos os valores com mais de uma ocorrência (ou seja: não exclusivo) em uma matriz

Com o JavaScript 1.6 / ECMAScript 5, você pode usar o método de filter nativo de uma matriz da seguinte maneira para obter uma matriz com valores exclusivos:

 function onlyUnique(value, index, self) { return self.indexOf(value) === index; } // usage example: var a = ['a', 1, 'a', 2, '1']; var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1'] 

O filter método nativo fará um loop na matriz e deixará apenas as inputs que passarem a function de retorno de chamada especificada onlyUnique .

onlyUnique verifica se o valor fornecido é o primeiro a ocorrer. Caso contrário, deve ser duplicado e não será copiado.

Essa solução funciona sem nenhuma biblioteca extra, como jQuery ou prototype.js.

Ele também funciona para matrizes com tipos de valores mistos.

Para navegadores antigos (filter methods nativos e indexOf você pode encontrar soluções de contorno na documentação do MDN para filter e indexOf .

Se você quiser manter a última ocorrência de um valor, simplesmente substitua indexOf por lastIndexOf .

Com ES6 pode ser reduzido para isso:

 // usage example: var myArray = ['a', 1, 'a', 2, '1']; var unique = myArray.filter((v, i, a) => a.indexOf(v) === i); // unique is ['a', 1, 2, '1'] 

Obrigado a Camilo Martin pela dica no comentário.

ES6 tem um object nativo Set para armazenar valores exclusivos. Para obter uma matriz com valores exclusivos, você pode fazer isso agora:

 var myArray = ['a', 1, 'a', 2, '1']; let unique = [...new Set(myArray)]; // unique is ['a', 1, 2, '1'] 

O construtor de Set pega um object iterável, como Array, e o operador spread ... transforma o conjunto de volta em um Array. Obrigado a Lukas Liese pela dica no comentário.

Resposta atualizada para ES6 / ES2015 : Usando o conjunto , a solução de linha única é:

 var items = [4,5,4,6,3,4,5,2,23,1,4,4,4] var uniqueItems = Array.from(new Set(items)) 

Que retorna

 [4, 5, 6, 3, 2, 23, 1] 

Como le_m sugeriu, isso também pode ser encurtado usando o operador spread , como

 var uniqueItems = [...new Set(items)] 

Você também pode usar o underscore.js .

 console.log(_.uniq([1, 2, 1, 3, 1, 4])); 
  

Eu percebo que esta pergunta já tem mais de 30 respostas. Mas eu li todas as respostas existentes primeiro e fiz minha própria pesquisa.

Eu dividi todas as respostas para 4 possíveis soluções:

  1. Use o novo recurso do ES6: [...new Set( [1, 1, 2] )];
  2. Use o object { } para evitar duplicatas
  3. Use matriz de ajuda [ ]
  4. Use filter + indexOf

Veja os códigos de amostra encontrados nas respostas:

Use o novo recurso do ES6: [...new Set( [1, 1, 2] )];

 function uniqueArray0(array) { var result = Array.from(new Set(array)); return result } 

Use o object { } para evitar duplicatas

 function uniqueArray1( ar ) { var j = {}; ar.forEach( function(v) { j[v+ '::' + typeof v] = v; }); return Object.keys(j).map(function(v){ return j[v]; }); } 

Use matriz de ajuda [ ]

 function uniqueArray2(arr) { var a = []; for (var i=0, l=arr.length; i 

Use filter + indexOf

 function uniqueArray3(a) { function onlyUnique(value, index, self) { return self.indexOf(value) === index; } // usage var unique = a.filter( onlyUnique ); // returns ['a', 1, 2, '1'] return unique; } 

E eu me perguntei qual é o mais rápido. Eu fiz exemplo de Planilha Google para testar funções. Nota: o ECMA 6 não está disponível no Planilhas Google, por isso não posso testá-lo.

Aqui está o resultado dos testes: insira a descrição da imagem aqui

Eu esperava ver que o código usando o object { } venceria porque usa o hash. Então, fico feliz que os testes mostraram os melhores resultados para esse algoritmo no Chrome e no IE. Graças a @rab pelo código .

Eu já encontrei um método legal que usa jQuery

 arr = $.grep(arr, function(v, k){ return $.inArray(v ,arr) === k; }); 

Nota: Este código foi retirado do post de perfuração de pato de Paul Irish – esqueci de dar crédito: P

Um forro, JavaScript puro

Com syntax ES6

list = list.filter((x, i, a) => a.indexOf(x) == i)

 x --> item in array i --> index of item a --> array reference, (in this case "list") 

insira a descrição da imagem aqui

Com syntax ES5

 list = list.filter(function (x, i, a) { return a.indexOf(x) == i; }); 

Compatibilidade do Navegador : IE9 +

Solução mais curta com ES6: [...new Set( [1, 1, 2] )];

Ou se você quiser modificar o protótipo do Array (como na pergunta original):

 Array.prototype.getUnique = function() { return [...new Set( [this] )]; }; 

O EcmaScript 6 é implementado apenas parcialmente em navegadores modernos no momento (agosto de 2015), mas o Babel tornou-se muito popular para transpilar o ES6 (e até o ES7) de volta para o ES5. Dessa forma, você pode escrever o código ES6 hoje!

Se você está se perguntando o que ... significa, é chamado de operador de propagação . From MDN : «O operador spread permite que uma expressão seja expandida em locais onde múltiplos argumentos (para chamadas de function) ou múltiplos elementos (para literais de array) são esperados». Como um Conjunto é iterável (e pode ter apenas valores exclusivos), o operador de dispersão expandirá o Conjunto para preencher a matriz.

Recursos para aprender ES6:

  • Explorando ES6 pelo Dr. Axel Rauschmayer
  • Pesquisa “ES6” dos boletins semanais do JS
  • ES6 em profundidade artigos do blog Mozilla Hacks

Solução mais simples:

 var arr = [1, 3, 4, 1, 2, 1, 3, 3, 4, 1]; console.log([...new Set(arr)]); 

A maneira mais simples e rápida (no Google Chrome) de fazer isso:

 Array.prototype.unique = function() { var a = []; for (var i=0, l=this.length; i 

Simplesmente passa por todos os itens da matriz, testa se esse item já está na lista e, se não estiver, empurra para a matriz que é retornada.

De acordo com jsPerf, essa function é a mais rápida das que eu encontrei em qualquer lugar - sinta-se à vontade para adicionar a sua própria.

A versão não protótipo:

 function uniques(arr) { var a = []; for (var i=0, l=arr.length; i 

Classificação

Quando também precisar classificar o array, o seguinte é o mais rápido:

 Array.prototype.sortUnique = function() { this.sort(); var last_i; for (var i=0;i 

ou não protótipo:

 function sortUnique(arr) { arr.sort(); var last_i; for (var i=0;i 

Isso também é mais rápido que o método acima na maioria dos navegadores que não são do Chrome.

DESEMPENHO SOMENTE! este código é provavelmente 10X mais rápido que todos os códigos aqui * funciona em todos os navegadores e também tem o menor impacto de memory … e mais

Se você não precisa reutilizar a matriz antiga, faça as outras operações necessárias antes de convertê-lo para exclusivo aqui é provavelmente a maneira mais rápida de fazer isso, também muito curto.

 var array=[1,2,3,4,5,6,7,8,9,0,1,2,1]; 

então você pode tentar isso

 var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 1]; function toUnique(a, b, c) { //array,placeholder,placeholder b = a.length; while (c = --b) while (c--) a[b] !== a[c] || a.splice(c, 1); return a // not needed ;) } console.log(toUnique(array)); //[3, 4, 5, 6, 7, 8, 9, 0, 2, 1] 

Muitas das respostas aqui podem não ser úteis para iniciantes. Se a deduplicação de uma matriz for difícil, eles realmente saberão sobre a cadeia de protótipos, ou mesmo sobre a jQuery?

Nos navegadores modernos, uma solução simples e limpa é armazenar dados em um conjunto , que é projetado para ser uma lista de valores exclusivos.

 const cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford']; const uniqueCars = Array.from(new Set(cars)); 

O Array.from é útil para converter o Set de volta para um Array, para que você tenha access fácil a todos os methods impressionantes (resources) que os arrays possuem. Existem também outras maneiras de fazer a mesma coisa. Mas você pode não precisar do Array.from , já que os Sets têm muitos resources úteis, como forEach .

Se você precisar dar suporte ao antigo Internet Explorer e, portanto, não puder usar Set, uma técnica simples é copiar os itens para uma nova matriz, verificando de antemão se eles já estão na nova matriz.

 // Create a list of cars, with duplicates. var cars = ['Volvo', 'Jeep', 'Volvo', 'Lincoln', 'Lincoln', 'Ford']; // Create a list of unique cars, to put a car in if we haven't already. var uniqueCars = []; // Go through each car, one at a time. cars.forEach(function (car) { // The code within the following block runs only if the // current car does NOT exist in the uniqueCars list // - aka prevent duplicates if (uniqueCars.indexOf(car) === -1) { // Since we now know we haven't seen this car before, // copy it to the end of the uniqueCars list. uniqueCars.push(car); } }); 

Para tornar isso instantaneamente reutilizável, vamos colocá-lo em uma function.

 function deduplicate(data) { if (data.length > 0) { var result = []; data.forEach(function (elem) { if (result.indexOf(elem) === -1) { result.push(elem); } }); return result; } } 

Então, para se livrar das duplicatas, faríamos isso agora.

 var uniqueCars = deduplicate(cars); 

A parte deduplicate(cars) tornase a coisa que nomeamos como resultado quando a function é concluída.

Apenas passe o nome de qualquer array que você goste.

Este protótipo getUnique não está totalmente correto, porque se eu tiver um Array como: ["1",1,2,3,4,1,"foo"] ele retornará ["1","2","3","4"] e "1" é string e 1 é um inteiro; Eles são diferentes.

Aqui está uma solução correta:

 Array.prototype.unique = function(a){ return function(){ return this.filter(a) } }(function(a,b,c){ return c.indexOf(a,b+1) < 0 }); 

usando:

 var foo; foo = ["1",1,2,3,4,1,"foo"]; foo.unique(); 

O acima irá produzir ["1",2,3,4,1,"foo"] .

 ["Defects", "Total", "Days", "City", "Defects"].reduce(function(prev, cur) { return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev; }, []); [0,1,2,0,3,2,1,5].reduce(function(prev, cur) { return (prev.indexOf(cur) < 0) ? prev.concat([cur]) : prev; }, []); 

Sem estender o Array.prototype (é considerado uma prática ruim) ou usar jquery / sublinhado, você pode simplesmente filter o array.

Mantendo a última ocorrência:

  function arrayLastUnique(array) { return array.filter(function (a, b, c) { // keeps last occurrence return c.indexOf(a, b + 1) < 0; }); }, 

ou primeira ocorrência:

  function arrayFirstUnique(array) { return array.filter(function (a, b, c) { // keeps first occurrence return c.indexOf(a) === b; }); }, 

Bem, é apenas o javascript ECMAScript 5+, que significa apenas IE9 +, mas é bom para um desenvolvimento em HTML / JS nativo (App da Windows Store, Firefox OS, Sencha, Phonegap, Titanium, ...).

Isso porque 0 é um valor falso em JavaScript.

this[i] será falso se o valor da matriz for 0 ou qualquer outro valor falso.

Se você estiver usando o framework Prototype, não há necessidade de fazer loops ‘for’, você pode usar http://www.prototypejs.org/api/array/uniq assim:

 var a = Array.uniq(); 

Que irá produzir um array duplicado sem duplicatas. Me deparei com sua pergunta pesquisando um método para contar registros de matriz distintos para depois

uniq ()

eu usei

Tamanho()

e houve o meu resultado simples. ps Desculpe se eu misstyped algo

edit: se você quiser escaping de registros indefinidos, você pode querer adicionar

compactar()

antes, assim:

 var a = Array.compact().uniq(); 
 Array.prototype.getUnique = function() { var o = {}, a = [] for (var i = 0; i < this.length; i++) o[this[i]] = 1 for (var e in o) a.push(e) return a } 

Não sei por que Gabriel Silveira escreveu a function dessa maneira, mas uma forma mais simples que funciona para mim tão bem e sem a minificação é:

 Array.prototype.unique = function() { return this.filter(function(value, index, array) { return array.indexOf(value, index + 1) < 0; }); }; 

ou no CoffeeScript:

 Array.prototype.unique = -> this.filter( (value, index, array) -> array.indexOf(value, index + 1) < 0 ) 

com es6 (e mantém a ordem):

 [...new Set(myArray)]; 

Faça um conjunto da matriz e, em seguida, inicialize a cópia superficial do conjunto no contêiner desejado.

 let array = [1,2,3,2,1]; let uniqueArray = [... new Set(array)]; 

Do blog de Shamasis Bhattacharya (O (2n) complexidade do tempo):

 Array.prototype.unique = function() { var o = {}, i, l = this.length, r = []; for(i=0; i 

Do blog de Paul Irish : melhoria em JQuery .unique() :

 (function($){ var _old = $.unique; $.unique = function(arr){ // do the default behavior only if we got an array of elements if (!!arr[0].nodeType){ return _old.apply(this,arguments); } else { // reduce the array to contain no dupes via grep/inArray return $.grep(arr,function(v,k){ return $.inArray(v,arr) === k; }); } }; })(jQuery); // in use.. var arr = ['first',7,true,2,7,true,'last','last']; $.unique(arr); // ["first", 7, true, 2, "last"] var arr = [1,2,3,4,5,4,3,2,1]; $.unique(arr); // [1, 2, 3, 4, 5] 

Encontrando valores exclusivos de Matriz no método simples

 function arrUnique(a){ var t = []; for(var x = 0; x < a.length; x++){ if(t.indexOf(a[x]) == -1)t.push(a[x]); } return t; } arrUnique([1,4,2,7,1,5,9,2,4,7,2]) // [1, 4, 2, 7, 5, 9] 

Para resolver o problema o inverso, pode ser útil não ter duplicado enquanto você carrega sua matriz, da mesma forma que o object Set faria isso, mas ainda não está disponível em todos os navegadores. Ele economiza memory e é mais eficiente se você precisar examinar seu conteúdo várias vezes.

 Array.prototype.add = function (elem) { if (this.indexOf(elem) == -1) { this.push(elem); } } 

Amostra:

 set = []; [1,3,4,1,2,1,3,3,4,1].forEach(function(x) { set.add(x); }); 

set = [1,3,4,2]

estranho isso não foi sugerido antes .. para remover duplicatas por chave de object ( id abaixo) em uma matriz, você pode fazer algo parecido com isto:

 const uniqArray = array.filter((obj, idx, arr) => ( arr.findIndex((o) => o.id === obj.id) === idx )) 

Existe uma maneira fácil de resolver essa tarefa por meio do ES6 – usando Set:

 let arr = [1, 1, 2, 2, 3, 3]; let deduped = [...new Set(arr)] // [1, 2, 3] 

Você também pode usar jQuery

 var a = [1,5,1,6,4,5,2,5,4,3,1,2,6,6,3,3,2,4]; // note: jQuery's filter params are opposite of javascript's native implementation :( var unique = $.makeArray($(a).filter(function(i,itm){ // note: 'index', not 'indexOf' return i == $(a).index(itm); })); // unique: [1, 5, 6, 4, 2, 3] 

Originalmente respondida em: jQuery function para obter todos os elementos exclusivos de um array?

Se alguém usando knockoutjs

 ko.utils.arrayGetDistinctValues() 

BTW tem olhar para todos os utilitários ko.utils.array* .

Eu descobri que a serialização da chave hash ajudou-me a conseguir esse trabalho para objects.

 Array.prototype.getUnique = function() { var hash = {}, result = [], key; for ( var i = 0, l = this.length; i < l; ++i ) { key = JSON.stringify(this[i]); if ( !hash.hasOwnProperty(key) ) { hash[key] = true; result.push(this[i]); } } return result; } 

Você também pode usar sugar.js:

 [1,2,2,3,1].unique() // => [1,2,3] [{id:5, name:"Jay"}, {id:6, name:"Jay"}, {id: 5, name:"Jay"}].unique('id') // => [{id:5, name:"Jay"}, {id:6, name:"Jay"}] 

Podemos fazer isso usando conjuntos ES6:

 var duplicatedArray = [1,2,3,4,5,1,1,1,2,3,4]; var uniqueArray = Array.from(new Set(duplicatedArray)); 

// A saída será

 uniqueArray = [1,2,3,4,5];