Remova duplicatas de uma matriz de objetos em JavaScript

374

Eu tenho um objeto que contém uma matriz de objetos.

things = new Object();

things.thing = new Array();

things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});

Gostaria de saber qual é o melhor método para remover objetos duplicados de uma matriz. Então, por exemplo, as coisas. Tudo se tornaria ...

{place:"here",name:"stuff"},
{place:"there",name:"morestuff"}
Travis
fonte
Você quer dizer como você para uma hashtable / objeto com todos os mesmos parâmetros sendo adicionados a uma matriz?
Matthew Lock
11
Mathew -> Se é mais simples impedir que um objeto duplicado seja adicionado à matriz em primeiro lugar, em vez de filtrá-lo mais tarde, sim, isso também seria bom.
Travis
2
Continua me surpreendendo como as pessoas nomeiam suas variáveis. Às vezes, acho que eles realmente querem complicar desnecessariamente. Next to see will aaaaa.aaaa.push(...):)
dazito 25/01

Respostas:

154

Um método primitivo seria:

var obj = {};

for ( var i=0, len=things.thing.length; i < len; i++ )
    obj[things.thing[i]['place']] = things.thing[i];

things.thing = new Array();
for ( var key in obj )
    things.thing.push(obj[key]);
aefxx
fonte
15
Você nunca deve usar o comprimento no loop for, porque isso atrasará tudo, calculando-o a cada iteração. Atribua-o a uma variável fora do loop e passe a variável em vez de things.thing.length.
precisa saber é o seguinte
12
@efefxx Eu não entendo muito bem essa função, como você lida com a situação de que o "lugar" é o mesmo, mas o nome é diferente, deve ser considerado duplamente ou não?
Kuan
2
Embora isso funcione, ele não cuida de uma matriz classificada, pois a busca de chaves nunca é garantida por ordem. Então, você acaba classificando novamente. Agora, suponha que a matriz não foi resolvido, mas ainda a sua ordem é importante, não há nenhuma maneira você pode ter certeza de que estadias ordem intacta
Deepak GM
11
@DeepakGM Você está absolutamente certo. A resposta não (necessariamente) preservará uma determinada ordem. Se isso é um requisito, deve-se procurar outra solução.
Aefxx
Como eu poderia modificar o exposto acima para remover objetos de uma matriz que contenha X e que não sejam enganados?
Ryan Holton
435

Que tal com um pouco de es6mágica?

things.thing = things.thing.filter((thing, index, self) =>
  index === self.findIndex((t) => (
    t.place === thing.place && t.name === thing.name
  ))
)

URL de referência

Uma solução mais genérica seria:

const uniqueArray = things.thing.filter((thing, index) => {
  const _thing = JSON.stringify(thing);
  return index === things.thing.findIndex(obj => {
    return JSON.stringify(obj) === _thing;
  });
});

Exemplo Stackblitz

Eydrian
fonte
81
Isso pode ser abreviado para:things.thing = things.thing.filter((thing, index, self) => self.findIndex(t => t.place === thing.place && t.name === thing.name) === index)
Josh Cole
Funciona perfeitamente! var uniqueArrayOfObjects = arrayOfObjects.filter (função (obj, índice, auto) {return index === self.findIndex (função (t) {return t ['obj-property'] === obj ['obj-property'] });}); Certifique-se de usar a sintaxe JS adequada.
Mohamed Salem Lamiri 14/11
Essas são a sintaxe JS adequada. O seu não está usando 1) funções de seta gorda 2) retorno implícito ou 3) notação de ponto. A sua é a sintaxe do ES5. Os outros são principalmente ES6 (ECMA2015). Todos são válidos em 2017. Veja o comentário de jaredwilli.
agm1984
8
@vsync basta dar resposta da @ BKM e colocá-lo em conjunto, uma solução genérica seria: const uniqueArray = arrayOfObjects.filter((object,index) => index === arrayOfObjects.findIndex(obj => JSON.stringify(obj) === JSON.stringify(object))); jsfiddle.net/x9ku0p7L/28
Eydrian
9
A chave aqui é que o método findIndex () retorna o índice do primeiro elemento; portanto, se houver um segundo elemento correspondente, ele nunca será encontrado e adicionado durante o filtro. Eu estava olhando para ele por um minuto :)
JBaczuk
111

Se você pode usar bibliotecas Javascript, como sublinhado ou lodash, eu recomendo dar uma olhada na _.uniqfunção em suas bibliotecas. De lodash:

_.uniq(array, [isSorted=false], [callback=_.identity], [thisArg])

Basicamente, você transmite a matriz que aqui é um objeto literal e o atributo que deseja remover duplicatas na matriz de dados original, assim:

var data = [{'name': 'Amir', 'surname': 'Rahnama'}, {'name': 'Amir', 'surname': 'Stevens'}];
var non_duplidated_data = _.uniq(data, 'name'); 

ATUALIZAÇÃO : Lodash agora introduziu um .uniqBytambém.

ambodi
fonte
3
@ Pveveen Pds: Eu disse alguma coisa sobre sublinhado no exemplo de código? Eu disse que 'lodash' tem essa função e o sublinhado tem funções semelhantes. Antes de votar, leia as respostas com atenção.
ambodi
// Lista objetos únicos usando _underscore.js holdingObject = _.uniq (holdingObject, function (item, chave, nome) {return item.name;});
praveenpds
26
Nota: agora você precisa usar em uniqByvez de uniq, por exemplo _.uniqBy(data, 'name')... documentação: lodash.com/docs#uniqBy
drmrbrewer
83

Eu tinha exatamente esse mesmo requisito, para remover objetos duplicados em uma matriz, com base em duplicatas em um único campo. Encontrei o código aqui: Javascript: Remover duplicatas da matriz de objetos

Portanto, no meu exemplo, estou removendo qualquer objeto da matriz que tenha um valor duplicado da sequência licenseNum.

var arrayWithDuplicates = [
    {"type":"LICENSE", "licenseNum": "12345", state:"NV"},
    {"type":"LICENSE", "licenseNum": "A7846", state:"CA"},
    {"type":"LICENSE", "licenseNum": "12345", state:"OR"},
    {"type":"LICENSE", "licenseNum": "10849", state:"CA"},
    {"type":"LICENSE", "licenseNum": "B7037", state:"WA"},
    {"type":"LICENSE", "licenseNum": "12345", state:"NM"}
];

function removeDuplicates(originalArray, prop) {
     var newArray = [];
     var lookupObject  = {};

     for(var i in originalArray) {
        lookupObject[originalArray[i][prop]] = originalArray[i];
     }

     for(i in lookupObject) {
         newArray.push(lookupObject[i]);
     }
      return newArray;
 }

var uniqueArray = removeDuplicates(arrayWithDuplicates, "licenseNum");
console.log("uniqueArray is: " + JSON.stringify(uniqueArray));

Os resultados:

uniqueArray é:

[{"type":"LICENSE","licenseNum":"10849","state":"CA"},
{"type":"LICENSE","licenseNum":"12345","state":"NM"},
{"type":"LICENSE","licenseNum":"A7846","state":"CA"},
{"type":"LICENSE","licenseNum":"B7037","state":"WA"}]
James Drinkard
fonte
11
Isso seria mais útil se a função também pudesse filtrar os objetos 'falsy'. for(var i in array) { if(array[i][prop]){ //valid lookupObject[array[i][prop]] = array[i]; } else { console.log('falsy object'); } }
Abdul Sadik Yalcin
Por que não reduzir a complexidade 0 (n) usando: for (let i in originalArray) { if (lookupObject[originalArray[i]['id']] === undefined) { newArray.push(originalArray[i]); } lookupObject[originalArray[i]['id']] = originalArray[i]; }
Tudor B.
essa é a melhor maneira, pois é importante saber o que você deseja que não seja duplicado. Agora isso pode ser feito através do redutor para os padrões e6?
Christian Matthew
70

Forros um mais curtos para ES6 +

Encontre os exclusivos idem uma matriz.

arr.filter((v,i,a)=>a.findIndex(t=>(t.id === v.id))===i)

Exclusivo por várias propriedades ( placee name)

arr.filter((v,i,a)=>a.findIndex(t=>(t.place === v.place && t.name===v.name))===i)

Exclusivo por todas as propriedades (isso será lento para matrizes grandes)

arr.filter((v,i,a)=>a.findIndex(t=>(JSON.stringify(t) === JSON.stringify(v)))===i)

Mantenha a última ocorrência.

arr.slice().reverse().filter((v,i,a)=>a.findIndex(t=>(t.id === v.id))===i).reverse()
galinhas
fonte
2
Magia, esta é a verdadeira resposta
Luis Contreras
48

Um revestimento usando Set

var things = new Object();

things.thing = new Array();

things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});

// assign things.thing to myData for brevity
var myData = things.thing;

things.thing = Array.from(new Set(myData.map(JSON.stringify))).map(JSON.parse);

console.log(things.thing)

Explicação:

  1. new Set(myData.map(JSON.stringify))cria um objeto Set usando os elementos myData com string.
  2. O objeto Set garantirá que cada elemento seja único.
  3. Em seguida, crio uma matriz com base nos elementos do conjunto criado usando Array.from.
  4. Finalmente, eu uso JSON.parse para converter o elemento stringified novamente em um objeto.
Mμ.
fonte
18
o problema sendo {a: 1, b: 2} não será igual a {b: 2, a: 1}
PirateApp
2
manter em mente que haveria um problema com propriedades de data
MarkosyanArtur
Essa linha cria valores nulos aleatórios com um objeto de linha que não existe na matriz original de objetos. Você pode por favor ajudar?
B1K
46

Usando o ES6 + em uma única linha, você pode obter uma lista exclusiva de objetos por chave:

const unique = [...new Map(arr.map(item => [item[key], item])).values()]

Pode ser colocado em uma função:

function getUniqueListBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()]
}

Aqui está um exemplo de trabalho:

const arr = [
    {place: "here",  name: "x", other: "other stuff1" },
    {place: "there", name: "x", other: "other stuff2" },
    {place: "here",  name: "y", other: "other stuff4" },
    {place: "here",  name: "z", other: "other stuff5" }
]

function getUniqueListBy(arr, key) {
    return [...new Map(arr.map(item => [item[key], item])).values()]
}

const arr1 = getUniqueListBy(arr, 'place')

console.log("Unique by place")
console.log(JSON.stringify(arr1))

console.log("\nUnique by name")
const arr2 = getUniqueListBy(arr, 'name')

console.log(JSON.stringify(arr2))

Como funciona

Primeiro, o array é remapeado de forma que possa ser usado como entrada para um mapa.

arr.map (item => [item [chave], item]);

o que significa que cada item da matriz será transformado em outra matriz com 2 elementos; a chave selecionada como primeiro elemento e o item inicial inteiro como segundo elemento, isso é chamado de entrada (por exemplo , entradas de matriz , entradas de mapa ). E aqui está o documento oficial com um exemplo mostrando como adicionar entradas de matriz no construtor Map.

Exemplo quando a chave é local :

[["here", {place: "here",  name: "x", other: "other stuff1" }], ...]

Em segundo lugar, passamos esse array modificado para o construtor Map e aqui está a mágica acontecendo. O mapa eliminará os valores das chaves duplicadas, mantendo apenas o último valor inserido da mesma chave. Nota : O mapa mantém a ordem de inserção. ( verifique a diferença entre mapa e objeto )

novo mapa (matriz de entrada mapeada acima)

Terceiro, usamos os valores do mapa para recuperar os itens originais, mas desta vez sem duplicatas.

novo Map (mappedArr) .values ​​()

E o último é adicionar esses valores em uma nova matriz nova, para que ela possa parecer a estrutura inicial e retornar isso:

return [... new Map (mappedArr) .values ​​()]

V. Sambor
fonte
Isso não responde à pergunta original, pois se trata de uma pesquisa id. A questão precisa que o objeto inteiro seja único em todos os campos como placeename
L. Holanda
Sua função ES6 parece muito concisa e prática. Você pode explicar um pouco mais? O que está acontecendo exatamente? As primeiras ou últimas duplicatas são removidas? Ou é aleatório, qual duplicado é removido? Isso seria útil, obrigado.
David Schumann
Até onde eu sei, é criado um mapa com o valor da propriedade como chave. Mas não é 100% como ou se a ordem da matriz é preservada.
David Schumann
11
Olá @DavidSchumann, atualizarei a resposta e explicarei como ela funciona. Mas, para uma resposta curta, a ordem é preservada e a primeira é removida ... Pense em como ela é inserida no mapa ... verifica se a chave já existe, ela será atualizada, portanto a última permanecerá
V Sambor 25/03
30

Aqui está outra opção para fazê-lo usando os métodos de iteração Array, se você precisar comparar apenas um campo de um objeto:

    function uniq(a, param){
        return a.filter(function(item, pos, array){
            return array.map(function(mapItem){ return mapItem[param]; }).indexOf(item[param]) === pos;
        })
    }

    uniq(things.thing, 'place');
Alex Kobylinski
fonte
Embora isso tenha um pedido maior que O (n²), isso se encaixa no meu caso de uso porque o tamanho da minha matriz sempre será menor que 30. Obrigado!
Sterex
24

um forro está aqui

let arr = [
  {id:1,name:"sravan ganji"},
  {id:2,name:"anu"},
  {id:4,name:"mammu"},
  {id:3,name:"sanju"},
  {id:3,name:"ram"},
];

console.log(Object.values(arr.reduce((acc,cur)=>Object.assign(acc,{[cur.id]:cur}),{})))

sravan ganji
fonte
11
Limpo e limpo, se você deseja remover objetos com um único valor duplicado, não tão limpo para objetos totalmente duplicados.
David Barker
22

Se você pode esperar para eliminar as duplicatas até depois de todas as adições, a abordagem típica é classificar primeiro a matriz e depois eliminar as duplicatas. A classificação evita a abordagem N * N de varrer a matriz para cada elemento à medida que você os percorre.

A função "eliminar duplicatas" geralmente é chamada de única ou uniq . Algumas implementações existentes podem combinar as duas etapas, por exemplo, o uniq do protótipo

Este post tem poucas idéias para tentar (e outras para evitar :-)) se sua biblioteca ainda não tiver uma ! Pessoalmente, acho este o mais direto:

    function unique(a){
        a.sort();
        for(var i = 1; i < a.length; ){
            if(a[i-1] == a[i]){
                a.splice(i, 1);
            } else {
                i++;
            }
        }
        return a;
    }  

    // Provide your own comparison
    function unique(a, compareFunc){
        a.sort( compareFunc );
        for(var i = 1; i < a.length; ){
            if( compareFunc(a[i-1], a[i]) === 0){
                a.splice(i, 1);
            } else {
                i++;
            }
        }
        return a;
    }
Maccullt
fonte
Isso não funcionará para objetos genéricos sem uma ordem de classificação natural.
Tim Down
É verdade que eu adicionei uma versão de comparação fornecida pelo usuário.
10388 MacCullt
Sua versão de comparação fornecida pelo usuário não funcionará porque, se sua função de comparação for function(_a,_b){return _a.a===_b.a && _a.b===_b.b;}, a matriz não será classificada.
graham.reeds
11
Essa é uma função de comparação inválida. From developer.mozilla.org/en/Core_JavaScript_1.5_Reference/… ... função compare (a, b) {if (a é menor que b por algum critério de pedido) return -1; se (a for maior que b pelo critério de pedido) retorne 1; // a deve ser igual a b return 0; } ...
maccullt 25/03
22

A maneira mais simples é usar filter:

var uniq = {}
var arr  = [{"id":"1"},{"id":"1"},{"id":"2"}]
var arrFiltered = arr.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));
console.log('arrFiltered', arrFiltered)

Alex Dykyі
fonte
6
É uma boa prática no Stack Overflow adicionar uma explicação sobre por que sua solução deve funcionar, especialmente como a sua é melhor do que as outras respostas. Para mais informações, leia Como responder .
Samuel Liew
Isso não responde à pergunta original, pois se trata de uma pesquisa id. A questão precisa que o objeto inteiro seja único em todos os campos como placeename
L. Holanda
17

Esta é uma maneira genérica de fazer isso: você passa uma função que testa se dois elementos de uma matriz são considerados iguais. Nesse caso, ele compara os valores das propriedades namee placedos dois objetos que estão sendo comparados.

Resposta ES5

function removeDuplicates(arr, equals) {
    var originalArr = arr.slice(0);
    var i, len, val;
    arr.length = 0;

    for (i = 0, len = originalArr.length; i < len; ++i) {
        val = originalArr[i];
        if (!arr.some(function(item) { return equals(item, val); })) {
            arr.push(val);
        }
    }
}

function thingsEqual(thing1, thing2) {
    return thing1.place === thing2.place
        && thing1.name === thing2.name;
}

var things = [
  {place:"here",name:"stuff"},
  {place:"there",name:"morestuff"},
  {place:"there",name:"morestuff"}
];

removeDuplicates(things, thingsEqual);
console.log(things);

Resposta original do ES3

function arrayContains(arr, val, equals) {
    var i = arr.length;
    while (i--) {
        if ( equals(arr[i], val) ) {
            return true;
        }
    }
    return false;
}

function removeDuplicates(arr, equals) {
    var originalArr = arr.slice(0);
    var i, len, j, val;
    arr.length = 0;

    for (i = 0, len = originalArr.length; i < len; ++i) {
        val = originalArr[i];
        if (!arrayContains(arr, val, equals)) {
            arr.push(val);
        }
    }
}

function thingsEqual(thing1, thing2) {
    return thing1.place === thing2.place
        && thing1.name === thing2.name;
}

removeDuplicates(things.thing, thingsEqual);
Tim Down
fonte
11
Dois objetos não serão avaliados iguais, mesmo que compartilhem as mesmas propriedades e valores.
Kennebec
Sim eu conheço. Mas, justamente, não consegui ler a pergunta corretamente: não havia percebido que eram objetos com propriedades idênticas que ele precisava eliminar. Vou editar minha resposta.
Tim Down
11
em vez de enquanto arrayContains- dentro usar Array.prototype..some método retorna true se um dos membros da matriz corresponder condição
MarkosyanArtur
13

Para adicionar mais um à lista. Usando ES6 e Array.reducecom Array.find.
Neste exemplo, filtrando objetos com base em uma guidpropriedade

let filtered = array.reduce((accumulator, current) => {
  if (! accumulator.find(({guid}) => guid === current.guid)) {
    accumulator.push(current);
  }
  return accumulator;
}, []);

Estendendo este para permitir a seleção de uma propriedade e compactá-lo em um liner:

const uniqify = (array, key) => array.reduce((prev, curr) => prev.find(a => a[key] === curr[key]) ? prev : prev.push(curr) && prev, []);

Para usá-lo, passe uma matriz de objetos e o nome da chave que você deseja desduplicar como um valor de sequência:

const result = uniqify(myArrayOfObjects, 'guid')
Pete B
fonte
11

Você também pode usar um Map:

const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());

Amostra completa:

const things = new Object();

things.thing = new Array();

things.thing.push({place:"here",name:"stuff"});
things.thing.push({place:"there",name:"morestuff"});
things.thing.push({place:"there",name:"morestuff"});

const dedupThings = Array.from(things.thing.reduce((m, t) => m.set(t.place, t), new Map()).values());

console.log(JSON.stringify(dedupThings, null, 4));

Resultado:

[
    {
        "place": "here",
        "name": "stuff"
    },
    {
        "place": "there",
        "name": "morestuff"
    }
]
Pragmateek
fonte
+1, agradável tho explicar um pouco mais o funcionamento interno de dedupThings seria bom - no lado do brilhante agora eu entendo reduzir: D
MimiEAM
11

Dang, crianças, vamos esmagar essa coisa, por que não?

let uniqIds = {}, source = [{id:'a'},{id:'b'},{id:'c'},{id:'b'},{id:'a'},{id:'d'}];
let filtered = source.filter(obj => !uniqIds[obj.id] && (uniqIds[obj.id] = true));
console.log(filtered);
// EXPECTED: [{id:'a'},{id:'b'},{id:'c'},{id:'d'}];

Cliff Hall
fonte
Isso não responde à pergunta original, pois se trata de uma pesquisa id. A questão precisa que o objeto inteiro seja único em todos os campos como placeename
L. Holanda
Este é um refinamento de uma generalização acima do problema. A pergunta original foi postada há 9 anos, então o pôster original provavelmente não está preocupado placee namehoje. Qualquer pessoa que esteja lendo este encadeamento está procurando uma maneira ideal de deduzir uma lista de objetos, e é uma maneira compacta de fazer isso.
Cliff Hall
11

Uma solução TypeScript

Isso removerá objetos duplicados e também preservará os tipos dos objetos.

function removeDuplicateObjects(array: any[]) {
  return [...new Set(array.map(s => JSON.stringify(s)))]
    .map(s => JSON.parse(s));
}

fonte
2
isso é ótimo e curto!
Mjjj #
E super lento também ...
L. Holanda
7

Considerando lodash.uniqWith

var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];

_.uniqWith(objects, _.isEqual);
// => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
Justin
fonte
Perfeito! Obrigado ;-)
DonFabiolas
11
Nem o uniq da lodash nem o uniqBy fizeram o truque, mas a sua solução. Obrigado! No entanto, forneça a fonte do seu código, se for uma cópia direta. lodash.com/docs/4.17.10#uniqWith
Manu CJ
5

Outra opção seria criar uma função indexOf personalizada, que compara os valores da propriedade escolhida para cada objeto e envolva-a em uma função de redução.

var uniq = redundant_array.reduce(function(a,b){
      function indexOfProperty (a, b){
          for (var i=0;i<a.length;i++){
              if(a[i].property == b.property){
                   return i;
               }
          }
         return -1;
      }

      if (indexOfProperty(a,b) < 0 ) a.push(b);
        return a;
    },[]);
ZeroSum
fonte
isso funcionou muito bem para mim - emparelhei isso com o lodash.isequalpacote npm como um comparador leve de objetos para executar uma filtragem de matriz exclusiva ... por exemplo, matriz distinta de objetos. Apenas trocados em if (_.isEqual(a[i], b)) {vez de olhar @ uma única propriedade
SliverNinja - MSFT
5

let myData = [{place:"here",name:"stuff"}, 
 {place:"there",name:"morestuff"},
 {place:"there",name:"morestuff"}];


let q = [...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];

console.log(q)

Uma linha usando ES6 e new Map().

// assign things.thing to myData
let myData = things.thing;

[...new Map(myData.map(obj => [JSON.stringify(obj), obj])).values()];

Detalhes: -

  1. Fazendo .map()na lista de dados e convertendo cada objeto individual em uma [key, value]matriz de pares (comprimento = 2), o primeiro elemento (chave) seria a stringifiedversão do objeto e o segundo (valor) seria umobject próprio.
  2. Adicionar a lista de matrizes criada acima a new Map()teria a chave como stringifiedobjeto e qualquer mesma adição de chave resultaria na substituição da chave já existente.
  3. Usar .values()daria ao MapIterator todos os valores em um mapa ( objno nosso caso)
  4. Por fim, spread ...operador para fornecer uma nova matriz com valores da etapa acima.
Savan Akbari
fonte
4

Aqui está uma solução para es6, na qual você deseja manter apenas o último item. Esta solução é funcional e compatível com o estilo Airbnb.

const things = {
  thing: [
    { place: 'here', name: 'stuff' },
    { place: 'there', name: 'morestuff1' },
    { place: 'there', name: 'morestuff2' }, 
  ],
};

const removeDuplicates = (array, key) => {
  return array.reduce((arr, item) => {
    const removed = arr.filter(i => i[key] !== item[key]);
    return [...removed, item];
  }, []);
};

console.log(removeDuplicates(things.thing, 'place'));
// > [{ place: 'here', name: 'stuff' }, { place: 'there', name: 'morestuff2' }]
Micah
fonte
Você pode remover a duplicata e também pode remover toda a duplicata com esse código. Agradável
sg28
4

removeDuplicates () recebe uma matriz de objetos e retorna uma nova matriz sem nenhum objeto duplicado (com base na propriedade id).

const allTests = [
  {name: 'Test1', id: '1'}, 
  {name: 'Test3', id: '3'},
  {name: 'Test2', id: '2'},
  {name: 'Test2', id: '2'},
  {name: 'Test3', id: '3'}
];

function removeDuplicates(array) {
  let uniq = {};
  return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true))
}

removeDuplicates(allTests);

Resultado esperado:

[
  {name: 'Test1', id: '1'}, 
  {name: 'Test3', id: '3'},
  {name: 'Test2', id: '2'}
];

Primeiro, configuramos o valor da variável uniq para um objeto vazio.

Em seguida, filtramos a matriz de objetos. O filtro cria uma nova matriz com todos os elementos que passam no teste implementado pela função fornecida.

return array.filter(obj => !uniq[obj.id] && (uniq[obj.id] = true));

Acima, usamos a funcionalidade de curto-circuito do &&. Se o lado esquerdo do && for avaliado como true, ele retornará o valor à direita do &&. Se o lado esquerdo for falso, ele retornará o que está no lado esquerdo do &&.

Para cada objeto (obj), checamos uniq para uma propriedade denominada valor de obj.id (nesse caso, na primeira iteração, verificaria a propriedade '1'.) Queremos o oposto do que ele retorna (verdadeiro ou false) e é por isso que usamos o! in! uniq [obj.id]. Se o uniq já tiver a propriedade id, ele retornará true, que será falso (!), Dizendo à função de filtro NÃO para adicionar esse objeto. No entanto, se não encontrar a propriedade obj.id, retornará false, que será avaliado como true (!) E retornará tudo à direita de &&, ou (uniq [obj.id] = true). Este é um valor verdadeiro, dizendo ao método de filtro para adicionar esse obj à matriz retornada e também adiciona a propriedade {1: true} ao uniq. Isso garante que qualquer outra instância obj com o mesmo ID não seja adicionada novamente.

MarkN
fonte
Talvez explique seu código e como ele responde à pergunta?
precisa saber é
Obrigado, mix3d. Eu adicionei esclarecimentos.
MarkN
Obrigado por explicar isso! Esta solução funciona para mim, e é semelhante a um par de outros postado aqui, embora eu não entendia o que estava acontecendo :)
Tim Molloy
3
let data = [
  {
    'name': 'Amir',
    'surname': 'Rahnama'
  }, 
  {
    'name': 'Amir',
    'surname': 'Stevens'
  }
];
let non_duplicated_data = _.uniqBy(data, 'name');
qzttt
fonte
9
Adicione uma explicação ao seu código para que futuros visitantes possam entender o que você está fazendo. Obrigado.
Bugs
A sua resposta é dependente de uma biblioteca de código externo ...
Taylor A. Leach
3

Eu acredito que uma combinação de reducecom JSON.stringifypara comparar perfeitamente objetos e seletivamente adicionar aqueles que ainda não estão no acumulador é uma maneira elegante.

Lembre-se de que isso JSON.stringifypode se tornar um problema de desempenho em casos extremos em que a matriz possui muitos objetos e eles são complexos, mas, na maioria das vezes , esse é o caminho mais curto a seguir.

var collection= [{a:1},{a:2},{a:1},{a:3}]

var filtered = collection.reduce((filtered, item) => {
  if( !filtered.some(filteredItem => JSON.stringify(filteredItem) == JSON.stringify(item)) )
    filtered.push(item)
  return filtered
}, [])

console.log(filtered)

Outra maneira de escrever o mesmo (mas menos eficiente):

collection.reduce((filtered, item) => 
  filtered.some(filteredItem => 
    JSON.stringify(filteredItem ) == JSON.stringify(item)) 
      ? filtered
      : [...filtered, item]
, [])
vsync
fonte
aquele que funciona para mim! obrigado!
javascript110899
2

Continuando a explorar as maneiras do ES6 de remover duplicatas da matriz de objetos: definir o thisArgargumento de Array.prototype.filterpara new Setfornece uma alternativa decente:

const things = [
  {place:"here",name:"stuff"},
  {place:"there",name:"morestuff"},
  {place:"there",name:"morestuff"}
];

const filtered = things.filter(function({place, name}) {

  const key =`${place}${name}`;

  return !this.has(key) && this.add(key);

}, new Set);

console.log(filtered);

No entanto, ele não funcionará com funções de seta () =>, pois thisestá vinculado ao seu escopo lexical.

Leonid Pyrlia
fonte
2

es6 magia em uma linha ... legível por isso!

// returns the union of two arrays where duplicate objects with the same 'prop' are removed
const removeDuplicatesWith = (a, b, prop) => a.filter(x => !b.find(y => x[prop] === y[prop]);
Josiah Coad
fonte
2

Solução simples com os métodos auxiliares de matriz 'reduzir' e 'localizar' do ES6

Funciona com eficiência e perfeitamente bem!

"use strict";

var things = new Object();
things.thing = new Array();
things.thing.push({
    place: "here",
    name: "stuff"
});
things.thing.push({
    place: "there",
    name: "morestuff"
});
things.thing.push({
    place: "there",
    name: "morestuff"
});

// the logic is here

function removeDup(something) {
    return something.thing.reduce(function (prev, ele) {
        var found = prev.find(function (fele) {
            return ele.place === fele.place && ele.name === fele.name;
        });
        if (!found) {
            prev.push(ele);
        }
        return prev;
    }, []);
}
console.log(removeDup(things));
KJ Sudarshan
fonte
Isso me ajudou muito, obrigado
jpisty 17/01
1

Se você não se importa em classificar sua matriz única posteriormente, esta seria uma solução eficiente:

things.thing
  .sort(((a, b) => a.place < b.place)
  .filter((current, index, array) =>
    index === 0 || current.place !== array[index - 1].place)

Dessa forma, você só precisa comparar o elemento atual com o elemento anterior na matriz. Classificar uma vez antes de filtrar ( O(n*log(n))) é mais barato do que procurar uma duplicata em toda a matriz para cada elemento da matriz ( O(n²)).

Elmo de Clemens
fonte
1

É uma maneira simples de remover a duplicidade da matriz de objetos.

Eu trabalho muito com dados e isso é útil para mim.

const data = [{name: 'AAA'}, {name: 'AAA'}, {name: 'BBB'}, {name: 'AAA'}];
function removeDuplicity(datas){
    return datas.filter((item, index,arr)=>{
    const c = arr.map(item=> item.name);
    return  index === c.indexOf(item.name)
  })
}

console.log(removeDuplicity(data))

imprimirá no console:

[[object Object] {
name: "AAA"
}, [object Object] {
name: "BBB"
}]
Juraj
fonte
Essa solução foi projetada para remover a duplicidade da matriz estática, mas quando você enviar dados do back-end para a matriz de dados, considere usar a opção substituir. Como nesse caso, o novo valor enviado para a matriz de dados será removido e o valor "antigo" ainda será armazenado na matriz de dados.
Juraj
1
str =[
{"item_id":1},
{"item_id":2},
{"item_id":2}
]

obj =[]
for (x in str){
    if(check(str[x].item_id)){
        obj.push(str[x])
    }   
}
function check(id){
    flag=0
    for (y in obj){
        if(obj[y].item_id === id){
            flag =1
        }
    }
    if(flag ==0) return true
    else return false

}
console.log(obj)

str é uma matriz de objetos. Existem objetos com o mesmo valor (aqui um pequeno exemplo, existem dois objetos com o mesmo item_id que 2). check (id) é uma função que verifica se algum objeto com o mesmo item_id existe ou não. se existir, retorne false, caso contrário retorne true. De acordo com esse resultado, coloque o objeto em uma nova matriz obj. A saída do código acima é [{"item_id":1},{"item_id":2}]

Bibin Jaimon
fonte
Adicione uma descrição
Mathews Sunny
@Billa Está tudo bem?
Bibin Jaimon
1

Você já ouviu falar da biblioteca Lodash? Eu recomendo esse utilitário, quando você realmente não deseja aplicar sua lógica ao código e usar o código já presente, otimizado e confiável.

Considere criar uma matriz como esta

things.thing.push({place:"utopia",name:"unicorn"});
things.thing.push({place:"jade_palace",name:"po"});
things.thing.push({place:"jade_palace",name:"tigress"});
things.thing.push({place:"utopia",name:"flying_reindeer"});
things.thing.push({place:"panda_village",name:"po"});

Observe que, se você quiser manter um atributo exclusivo, poderá muito bem fazer isso usando a biblioteca lodash. Aqui, você pode usar _.uniqBy

.uniqBy (matriz, [iteratee = .identity])

Este método é como _.uniq (que retorna uma versão sem duplicação de uma matriz, na qual apenas a primeira ocorrência de cada elemento é mantida), exceto que ele aceita o iteratee que é chamado para cada elemento na matriz para gerar o critério pelo qual a exclusividade é calculada.

Portanto, por exemplo, se você deseja retornar uma matriz com atributo exclusivo de 'place'

_.uniqBy (things.thing, 'place')

Da mesma forma, se você deseja um atributo exclusivo como 'nome'

_.uniqBy (things.thing, 'name')

Espero que isto ajude.

Felicidades!

Mayank Gangwal
fonte