Como remover objetos iguais de um array

Olá pessoal, estou com uma dúvida, não encontrei nada que me ajudasse com isso, a minha dúvida é:

Eu tenho o seguinte objeto:

[
  {
    "position": 1,
    "id": ""
  },
  {
    "position": 2,
    "id": ""
  },
  {
    "position": 3,
    "id": ""
  },
  {
    "position": 4,
    "id": ""
  },
  {
    "position": 3,
    "id": "123"
  },
  {
    "position": 4,
    "id": "456"
  }
]

Eu preciso verificar qual deles é igual e que possuí um id, esse é o retorno que espero:

[
  {
    "position": 1,
    "id": ""
  },
  {
    "position": 2,
    "id": ""
  },
  {
    "position": 3,
    "id": "123"
  },
  {
    "position": 4,
    "id": "456"
  }
]

Parece algo tão simples, mas não estou conseguindo, se puder me ajudar <3

1 curtida

Você pode usar o reduce junto do findIndex para fazer o agrupamento do Array, depois é só verificar se o item que está sendo agrupado possui um valor de id, se possuir, você substitui o valor anterior pelo novo.

const entrada = [
  {
    position: 1,
    id: "",
  },
  {
    position: 2,
    id: "",
  },
  {
    position: 3,
    id: "",
  },
  {
    position: 4,
    id: "",
  },
  {
    position: 3,
    id: "123",
  },
  {
    position: 4,
    id: "456",
  },
]

const saida = entrada.reduce((acc, cur) => {
  const index = acc.findIndex((value) => value.position === cur.position)

  if (index < 0) {
    acc.push(cur)
  } else {
    if (cur.id) {
      acc[index] = cur
    }
  }

  return acc
}, [])

console.log(saida)
2 curtidas

Com um simples for, com a busca pelo indice e uma comparação você consegue o efeito esperado:

const data = [
  {
    "position": 1,
    "id": ""
  },
  {
    "position": 2,
    "id": ""
  },
  {
    "position": 3,
    "id": "456"
  },
  {
    "position": 4,
    "id": ""
  },
  {
    "position": 3,
    "id": "123"
  },
  {
    "position": 4,
    "id": "456"
  }
];

let items = [];

for(let i = 0; i < data.length; i++) {
  let c = items
  	   .findIndex(function(f){ return f.position==data[i].position});  	
  if (c > -1) {
  	items[c] = data[i];
  } else {
  	items.push(data[i]);
  }
}

console.log(items);

Ela intera item a item (isso pode até ser melhorado), busca o indice pela chave position, se já houver sobreponhe os itens já adicionados e se não adicionado pelo método push.

1 curtida

Só uns detalhes pra complementar…

Apesar de ter funcionado, tem um porém (que pode ou não fazer diferença, dependendo do caso) em usar findIndex: este método sempre percorre o array desde o início, até encontrar o elemento que satisfaça a condição indicada. Isso quer dizer que, dependendo do caso, pode não ser a forma mais eficiente.

No caso de arrays pequenos não vai fazer diferença, afinal, para poucos dados, tudo é rápido. Mas no caso de arrays grandes, já começa a fazer diferença (veja o comparativo no final).

No seu caso, como você quer sobrescrever os valores repetidos (ou seja, pegar sempre a última ocorrência de position), não precisa ficar verificando toda a hora com findIndex. Uma alternativa é usar um objeto, usando o position como chave, e no final pegar somente os valores:

const data = [
  { "position": 1, "id": "" }, { "position": 2, "id": "" }, { "position": 3, "id": "456" },
  { "position": 4, "id": "" }, { "position": 3, "id": "123" }, { "position": 4, "id": "456" }
];

let items = {};
for (let i = 0; i < data.length; i++) {
    items[data[i].position] = data[i];
}
items = Object.values(items);
console.log(items);

Assim, se algum elemento tiver o mesmo position, ele é sobrescrito. No final, o objeto contém apenas uma ocorrência de cada position, sendo que em caso de repetições, apenas a última ocorrência estará nele. Assim, eu não preciso verificar se o elemento já existe (se não existir, ele é criado, se já existir, ele é sobrescrito).

Por fim, pego somente os valores (usando Object.values), resultando no array desejado.


Outra forma é usar um Map, que funciona de maneira similar:

const data = [
  { "position": 1, "id": "" }, { "position": 2, "id": "" }, { "position": 3, "id": "456" },
  { "position": 4, "id": "" }, { "position": 3, "id": "123" }, { "position": 4, "id": "456" }
];

let items = new Map();
for (let i = 0; i < data.length; i++) {
    items.set(data[i].position, data[i]);
}
items = Array.from(items.values());
console.log(items);

Comparativo

Como já dito, para arrays pequenos não faz diferença, inclusive neste caso o findIndex foi até mais rápido (veja aqui o teste).

Mas para arrays grandes, a diferença é brutal, veja aqui como a situação se inverte e as soluções com Object.values e Map passam a ser mais rápidas.

Isso acontece provavelmente porque existe um custo inicial para se criar o objeto e o Map (além de pegar somente os valores no final). Mas conforme o array cresce, este custo se paga e a solução fica muito mais rápida.

Mas como eu já disse, se você só vai ter poucos arrays pequenos, a diferença não será significativa. De qualquer forma, é importante saber que há outras maneiras de resolver, caso um dia você se depare com situações em que os arrays são maiores e a performance seja um ponto crítico.

2 curtidas

Ele percorrer toda, não é verdade, ele vai pecorrer até satisfazer a um determinada expressão, é bom também deixar claro as observações, ou seja, ele vai procurando até encontrar, claro que isso é um fator importante também. A ordenação pode ser executada antes, o custo é complicado dizer, depende do tamanho e também a comparação.
Geralmente não se traz dados grandes para execução, e sim paginados, então, vários fatores deve ser previstos.

Site: Array.prototype.findIndex() - JavaScript | MDN
The findIndex() method returns the index of the first element in an array that satisfies the provided testing function. If no elements satisfy the testing function, -1 is returned.

Tradução google:
O método retorna o índice do primeiro elemento em uma matriz que satisfaz a função de teste fornecida. Se nenhum elemento satisfizer a função de teste, -1 será retornado.

1 curtida

Mas eu não disse que percorre o array todo, eu disse que percorre até encontrar o elemento:

1 curtida