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

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

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


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.


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

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

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.

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 ​​()]


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

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}),{})))


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

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)


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

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

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

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


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

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

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

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.

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

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.


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

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]
, [])

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.


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

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

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²)).


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

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


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!

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.