javascript Remover valores duplicados da matriz JS




15 Answers

Maneira "inteligente", mas ingênua

uniqueArray = a.filter(function(item, pos) {
    return a.indexOf(item) == pos;
})

Basicamente, nós iteramos sobre o array e, para cada elemento, verificamos se a primeira posição desse elemento no array é igual à posição atual. Obviamente, essas duas posições são diferentes para elementos duplicados.

Usando o parâmetro 3rd ("this array") do retorno de chamada do filtro, podemos evitar o fechamento da variável array:

uniqueArray = a.filter(function(item, pos, self) {
    return self.indexOf(item) == pos;
})

Embora conciso, esse algoritmo não é particularmente eficiente para grandes matrizes (tempo quadrático).

Hashtables para o resgate

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

É assim que geralmente é feito. A ideia é colocar cada elemento em um hashtable e, em seguida, verificar sua presença instantaneamente. Isso nos dá tempo linear, mas tem pelo menos dois inconvenientes:

  • Como as chaves hash só podem ser strings em JavaScript, esse código não distingue números e "strings numéricas". Ou seja, uniq([1,"1"]) retornará apenas [1]
  • pelo mesmo motivo, todos os objetos serão considerados iguais: uniq([{foo:1},{foo:2}]) retornará apenas [{foo:1}] .

Dito isso, se seus arrays contiverem apenas primitivos e você não se importar com os tipos (por exemplo, sempre são números), essa solução é ideal.

O melhor dos dois mundos

Uma solução universal combina as duas abordagens: usa pesquisas de hash para primitivos e pesquisa linear de objetos.

function uniq(a) {
    var prims = {"boolean":{}, "number":{}, "string":{}}, objs = [];

    return a.filter(function(item) {
        var type = typeof item;
        if(type in prims)
            return prims[type].hasOwnProperty(item) ? false : (prims[type][item] = true);
        else
            return objs.indexOf(item) >= 0 ? false : objs.push(item);
    });
}

classificar | uniq

Outra opção é classificar primeiro o array e, em seguida, remover cada elemento igual ao precedente:

function uniq(a) {
    return a.sort().filter(function(item, pos, ary) {
        return !pos || item != ary[pos - 1];
    })
}

Novamente, isso não funciona com objetos (porque todos os objetos são iguais para sort ). Além disso, alteramos silenciosamente o array original como um efeito colateral - não é bom! No entanto, se a sua entrada já estiver ordenada, este é o caminho a seguir (apenas remova a sort acima).

Exclusivo por ...

Às vezes, é desejável uniquificar uma lista com base em alguns critérios além da igualdade, por exemplo, para filtrar objetos diferentes, mas compartilhar alguma propriedade. Isso pode ser feito elegantemente, passando um retorno de chamada. Esse retorno de chamada "chave" é aplicado a cada elemento e os elementos com "chaves" iguais são removidos. Como se espera que a key retorne uma tabela hash primitiva, ela funcionará bem aqui:

function uniqBy(a, key) {
    var seen = {};
    return a.filter(function(item) {
        var k = key(item);
        return seen.hasOwnProperty(k) ? false : (seen[k] = true);
    })
}

Uma key() particularmente útil key() é JSON.stringify que removerá objetos que são fisicamente diferentes, mas "parecem" iguais:

a = [[1,2,3], [4,5,6], [1,2,3]]
b = uniqBy(a, JSON.stringify)
console.log(b) // [[1,2,3], [4,5,6]]

Se a key não for primitiva, você precisará recorrer à pesquisa linear:

function uniqBy(a, key) {
    var index = [];
    return a.filter(function (item) {
        var k = key(item);
        return index.indexOf(k) >= 0 ? false : index.push(k);
    });
}

No ES6, você pode usar um Set :

function uniqBy(a, key) {
    var seen = new Set();
    return a.filter(item => {
        var k = key(item);
        return seen.has(k) ? false : seen.add(k);
    });
}

ou um Map :

function uniqBy(a, key) {
    return [
        ...new Map(
            myArr.map(x => [key(x), x])
        ).values()
    ]
}

que ambos também trabalham com chaves não primitivas.

Bibliotecas

Tanto o underscore quanto o Lo-Dash fornecem métodos uniq . Seus algoritmos são basicamente semelhantes ao primeiro snippet acima e resumem-se a isso:

var result = [];
a.forEach(function(item) {
     if(result.indexOf(item) < 0) {
         result.push(item);
     }
});

Isso é quadrático, mas há itens adicionais interessantes, como o envolvimento de indexOf nativo, a capacidade de uniqify por uma chave ( iteratee em sua linguagem) e otimizações para matrizes já classificadas.

Se você está usando o jQuery e não suporta nada sem um dólar antes dele, é assim:

  $.uniqArray = function(a) {
        return $.grep(a, function(item, pos) {
            return $.inArray(item, a) === pos;
        });
  }

que é, novamente, uma variação do primeiro trecho.

atuação

Chamadas de funções são caras em Javascript, portanto as soluções acima, tão concisas quanto são, não são particularmente eficientes. Para desempenho máximo, substitua o filter por um loop e elimine outras chamadas de função:

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

Este pedaço de código feio faz o mesmo que o trecho nº 3 acima, mas uma ordem de grandeza mais rápida (a partir de 2017 é apenas duas vezes mais rápido - o pessoal da JS está fazendo um ótimo trabalho!)

function uniq(a) {
    var seen = {};
    return a.filter(function(item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

function uniq_fast(a) {
    var seen = {};
    var out = [];
    var len = a.length;
    var j = 0;
    for(var i = 0; i < len; i++) {
         var item = a[i];
         if(seen[item] !== 1) {
               seen[item] = 1;
               out[j++] = item;
         }
    }
    return out;
}

/////

var r = [0,1,2,3,4,5,6,7,8,9],
    a = [],
    LEN = 1000,
    LOOPS = 1000;

while(LEN--)
    a = a.concat(r);

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq(a);
document.write('<br>uniq, ms/loop: ' + (new Date() - d)/LOOPS)

var d = new Date();
for(var i = 0; i < LOOPS; i++)
    uniq_fast(a);
document.write('<br>uniq_fast, ms/loop: ' + (new Date() - d)/LOOPS)

ES6

O ES6 fornece o objeto Set , o que torna as coisas muito mais fáceis:

function uniq(a) {
   return Array.from(new Set(a));
}

ou

let uniq = a => [...new Set(a)];

Observe que, diferentemente do python, os conjuntos ES6 são iterados em ordem de inserção, portanto, esse código preserva a ordem do array original.

No entanto, se você precisar de uma matriz com elementos exclusivos, por que não usar conjuntos desde o início?

Geradores

Uma versão "preguiçosa" baseada em gerador do uniq pode ser construída na mesma base:

  • pegue o próximo valor do argumento
  • se já foi visto, pule
  • caso contrário, apresente-o e adicione-o ao conjunto de valores já vistos

function* uniqIter(a) {
    let seen = new Set();

    for (let x of a) {
        if (!seen.has(x)) {
            seen.add(x);
            yield x;
        }
    }
}

// example:

function* randomsBelow(limit) {
    while (1)
        yield Math.floor(Math.random() * limit);
}

// note that randomsBelow is endless

count = 20;
limit = 30;

for (let r of uniqIter(randomsBelow(limit))) {
    console.log(r);
    if (--count === 0)
        break
}

// exercise for the reader: what happens if we set `limit` less than `count` and why

javascript arrays duplicates unique

Esta questão já tem uma resposta aqui:

Eu tenho um array JavaScript muito simples que pode ou não conter duplicatas.

var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

Eu preciso remover as duplicatas e colocar os valores exclusivos em uma nova matriz.

Eu poderia apontar para todos os códigos que eu tentei, mas eu acho que é inútil porque eles não funcionam. Aceito soluções da jQuery também.

Pergunta semelhante:




Baunilha JS: Remover duplicatas usando um objeto como um conjunto

Você sempre pode tentar colocá-lo em um objeto e, em seguida, iterar por meio de suas chaves:

function remove_duplicates(arr) {
    var obj = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        obj[arr[i]] = true;
    }
    for (var key in obj) {
        ret_arr.push(key);
    }
    return ret_arr;
}

Baunilha JS: Remover duplicatas, rastreando valores já vistos (ordem segura)

Ou, para uma versão segura, use um objeto para armazenar todos os valores vistos anteriormente e verifique os valores antes de adicioná-los a uma matriz.

function remove_duplicates_safe(arr) {
    var seen = {};
    var ret_arr = [];
    for (var i = 0; i < arr.length; i++) {
        if (!(arr[i] in seen)) {
            ret_arr.push(arr[i]);
            seen[arr[i]] = true;
        }
    }
    return ret_arr;

}

ECMAScript 6: Use a nova estrutura de dados Set (segura para pedidos)

O ECMAScript 6 adiciona o novo Set Data-Structure, que permite armazenar valores de qualquer tipo. Set.values retorna elementos no pedido de inserção.

function remove_duplicates_es6(arr) {
    let s = new Set(arr);
    let it = s.values();
    return Array.from(it);
}

Exemplo de uso:

a = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];

b = remove_duplicates(a);
// b:
// ["Adam", "Carl", "Jenny", "Matt", "Mike", "Nancy"]

c = remove_duplicates_safe(a);
// c:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]

d = remove_duplicates_es6(a);
// d:
// ["Mike", "Matt", "Nancy", "Adam", "Jenny", "Carl"]



Uma versão de linha única usando o filtro de matriz e as funções indexOf:

arr = arr.filter (function (value, index, array) { 
    return array.indexOf (value) == index;
});



Uma linha:

let names = ['Mike','Matt','Nancy','Adam','Jenny','Nancy','Carl', 'Nancy'];
let dup = [...new Set(names)];
console.log(dup);



use Array.filter() assim

var actualArr = ['Apple', 'Apple', 'Banana', 'Mango', 'Strawberry', 'Banana'];

console.log('Actual Array: ' + actualArr);

var filteredArr = actualArr.filter(function(item, index) {
  if (actualArr.indexOf(item) == index)
    return item;
});

console.log('Filtered Array: ' + filteredArr);

isso pode ser reduzido em ES6 para

actualArr.filter((item,index,self) => self.indexOf(item)==index);

Here está uma boa explicação do Array.filter()




Mais simples que eu já corri até agora. Em es6.

 var names = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl", "Mike", "Nancy"]

 var noDupe = Array.from(new Set(names))

Set




O seguinte é mais de 80% mais rápido do que o método jQuery listado (veja os testes abaixo). É uma resposta de uma pergunta semelhante há alguns anos atrás. Se eu me deparar com a pessoa que originalmente propôs, vou postar crédito. Puro JS.

var temp = {};
for (var i = 0; i < array.length; i++)
  temp[array[i]] = true;
var r = [];
for (var k in temp)
  r.push(k);
return r;

Minha comparação de caso de teste: http://jsperf.com/remove-duplicate-array-tests




Aqui está uma resposta simples para a pergunta.

var names = ["Alex","Tony","James","Suzane", "Marie", "Laurence", "Alex", "Suzane", "Marie", "Marie", "James", "Tony", "Alex"];
var uniqueNames = [];

    for(var i in names){
        if(uniqueNames.indexOf(names[i]) === -1){
            uniqueNames.push(names[i]);
        }
    }



Uma técnica simples, mas eficaz, é usar o filtermétodo em combinação com o filtro function(value, index){ return this.indexOf(value) == index }.

Exemplo de código:

var data = [2,3,4,5,5,4];
var filter = function(value, index){ return this.indexOf(value) == index };
var filteredData = data.filter(filter, data );

document.body.innerHTML = '<pre>' + JSON.stringify(filteredData, null, '\t') +  '</pre>';

Veja também este violino .




aqui está o método simples sem que nenhuma biblioteca especial seja uma função especial,

name_list = ["Mike","Matt","Nancy","Adam","Jenny","Nancy","Carl"];
get_uniq = name_list.filter(function(val,ind) { return name_list.indexOf(val) == ind; })

console.log("Original name list:"+name_list.length, name_list)
console.log("\n Unique name list:"+get_uniq.length, get_uniq)




Então as opções são:

let a = [11,22,11,22];
let b = []


b = [ ...new Set(a) ];     
// b = [11, 22]

b = Array.from( new Set(a))   
// b = [11, 22]

b = a.filter((val,i)=>{
  return a.indexOf(val)==i
})                        
// b = [11, 22]



for (i=0; i<originalArray.length; i++) {  
    if (!newArray.includes(originalArray[i])) {
        newArray.push(originalArray[i]); 
    }
}



$(document).ready(function() {

    var arr1=["dog","dog","fish","cat","cat","fish","apple","orange"]

    var arr2=["cat","fish","mango","apple"]

    var uniquevalue=[];
    var seconduniquevalue=[];
    var finalarray=[];

    $.each(arr1,function(key,value){

       if($.inArray (value,uniquevalue) === -1)
       {
           uniquevalue.push(value)

       }

    });

     $.each(arr2,function(key,value){

       if($.inArray (value,seconduniquevalue) === -1)
       {
           seconduniquevalue.push(value)

       }

    });

    $.each(uniquevalue,function(ikey,ivalue){

        $.each(seconduniquevalue,function(ukey,uvalue){

            if( ivalue == uvalue)

            {
                finalarray.push(ivalue);
            }   

        });

    });
    alert(finalarray);
});






Esta foi apenas outra solução, mas diferente do resto.

function diffArray(arr1, arr2) {
  var newArr = arr1.concat(arr2);
  newArr.sort();
  var finalArr = [];
  for(var i = 0;i<newArr.length;i++) {
   if(!(newArr[i] === newArr[i+1] || newArr[i] === newArr[i-1])) {
     finalArr.push(newArr[i]);
   } 
  }
  return finalArr;
}



Related