Supondo que tenho o seguinte:
var array =
[
{"name":"Joe", "age":17},
{"name":"Bob", "age":17},
{"name":"Carl", "age": 35}
]
Qual é a melhor maneira de obter uma matriz de todas as idades distintas, para que eu obtenha uma matriz de resultados de:
[17, 35]
Existe alguma maneira de estruturar alternativamente os dados ou um método melhor, de modo que não precise iterar através de cada matriz verificando o valor de "idade" e verificando se existe outra matriz em relação a outra matriz e adicioná-lo, se não?
Se houvesse alguma maneira, eu poderia retirar as idades distintas sem iterar ...
Atual maneira ineficiente que gostaria de melhorar ... Se isso significa que, em vez de "matriz" ser uma matriz de objetos, mas um "mapa" de objetos com alguma chave exclusiva (ou seja, "1,2,3") que seria ok também Estou apenas procurando a maneira mais eficiente de desempenho.
A seguir, é como eu faço isso atualmente, mas para mim, a iteração parece pouco exigente em termos de eficiência, mesmo que funcione ...
var distinct = []
for (var i = 0; i < array.length; i++)
if (array[i].age not in distinct)
distinct.push(array[i].age)
fonte
Set
objetomap
es são um desperdício. Este trabalho assume apenas uma.reduce()
etapa simples .Respostas:
Se esse fosse o PHP, eu construí uma matriz com as chaves e pegava
array_keys
no final, mas JS não tem esse luxo. Em vez disso, tente o seguinte:fonte
array_unique
compararia o item inteiro, não apenas a idade, como é solicitado aqui.flags = {}
é melhor do queflags = []
age
é um relativamente pequeno número inteiro (<120 certamente)Se você estiver usando o ES6 / ES2015 ou posterior, poderá fazê-lo desta maneira:
Aqui está um exemplo de como fazê-lo.
fonte
TypeError: (intermediate value).slice is not a function
usando ES6
fonte
array.filter((value, index, self) => self.map(x => x.age).indexOf(value.age) == index)
Você pode usar uma abordagem de dicionário como esta. Basicamente, você atribui o valor que deseja diferenciar como chave no "dicionário" (aqui usamos uma matriz como objeto para evitar o modo de dicionário). Se a chave não existir, adicione esse valor como distinto.
Aqui está uma demonstração de trabalho:
Será O (n) onde n é o número de objetos na matriz emm é o número de valores únicos. Não existe uma maneira mais rápida que O (n) porque você deve inspecionar cada valor pelo menos uma vez.
A versão anterior disso usava um objeto, e para dentro. Eles eram de natureza menor e, desde então, foram atualizados de maneira menor acima. No entanto, o motivo de um aparente avanço no desempenho entre as duas versões no jsperf original foi devido ao tamanho da amostra de dados ser tão pequeno. Portanto, a principal comparação na versão anterior foi analisar a diferença entre o uso interno do mapa e do filtro e as pesquisas no modo de dicionário.
Atualizei o código acima, conforme observado, no entanto, também atualizei o jsperf para examinar 1000 objetos em vez de 3. 3 negligenciou muitas das armadilhas de desempenho envolvidas ( jsperf obsoleto ).
atuação
https://jsperf.com/filter-vs-dictionary-more-data Quando executei este dicionário, foi 96% mais rápido.
fonte
if( typeof(unique[array[i].age]) == "undefined"){ distinct.push(array[i].age); unique[array[i].age] = 0; }
É assim que você resolveria isso usando o novo conjunto via ES6 para texto datilografado em 25 de agosto de 2017
fonte
Usando os recursos do ES6, você pode fazer algo como:
fonte
const uniqueObjects = [ ...new Set( array.map( obj => obj.age) ) ].map( age=> { return array.find(obj => obj.age === age) } )
Acabei de mapear e remover dups:
Edit: Aight! Não é a maneira mais eficiente em termos de desempenho, mas a IMO mais simples e legível. Se você realmente se preocupa com a micro-otimização ou possui enormes quantidades de dados, um
for
loop regular será mais "eficiente".fonte
if
s. você obterá resultados muito diferentes com três milhões.Exemplo ES6
fonte
Para quem deseja retornar objeto com todas as propriedades exclusivas por chave
fonte
Já existem muitas respostas válidas, mas eu queria adicionar uma que use apenas o
reduce()
método, pois é limpo e simples.Use-o assim:
fonte
A
forEach
versão da resposta do @ travis-j (útil em navegadores modernos e no mundo do Node JS):34% mais rápido no Chrome v29.0.1547: http://jsperf.com/filter-versus-dictionary/3
E uma solução genérica que usa uma função de mapeador (um pouco mais lenta que o mapa direto, mas isso é esperado):
fonte
Comecei a colocar o Underscore em todos os novos projetos por padrão, apenas para nunca ter que pensar nesses pequenos problemas de troca de dados.
Produz
[17, 35]
.fonte
Aqui está outra maneira de resolver isso:
Não faço ideia do quão rápido essa solução é comparada às outras, mas gosto do visual mais limpo. ;-)
EDIT: Ok, o acima parece ser a solução mais lenta de todos aqui.
Criei um caso de teste de desempenho aqui: http://jsperf.com/distinct-values-from-array
Em vez de testar para as idades (Inteiros), optei por comparar os nomes (Strings).
O método 1 (solução da TS) é muito rápido. Curiosamente, o Método 7 supera todas as outras soluções. Aqui, acabei de me livrar do .indexOf () e usei uma implementação "manual", evitando a chamada de função em loop:
A diferença de desempenho usando o Safari e Firefox é incrível, e parece que o Chrome faz o melhor trabalho de otimização.
Não sei exatamente por que os trechos acima são tão rápidos em comparação com os outros, talvez alguém mais sábio que eu tenha uma resposta. ;-)
fonte
usando lodash
fonte
Usando o Lodash
Retorna [17,35]
fonte
fonte
underscore.js
_.uniq(_.pluck(array,"age"))
fonte
Aqui está uma solução versátil que usa reduzir, permite mapear e mantém a ordem de inserção.
itens : uma matriz
mapeador : uma função unária que mapeia o item para os critérios ou vazia para mapear o próprio item.
Uso
Você pode adicionar isso ao seu protótipo Array e deixar de fora o parâmetro items se esse for o seu estilo ...
Você também pode usar um conjunto em vez de uma matriz para acelerar a correspondência.
fonte
fonte
Acabei de encontrar isso e achei útil
Novamente, usando sublinhado , se você tiver um objeto como este
ele fornecerá apenas os objetos exclusivos.
O que acontece aqui é que
indexBy
retorna um mapa como estee apenas por ser um mapa, todas as chaves são únicas.
Então, estou apenas mapeando essa lista de volta para a matriz.
Caso você precise apenas dos valores distintos
Lembre-se de que o valor
key
é retornado como uma sequência; portanto, se você precisar de números inteiros, deve fazerfonte
Eu acho que você está procurando a função groupBy (usando Lodash)
produz resultado:
Demonstração do jsFiddle: http://jsfiddle.net/4J2SX/201/
fonte
Se, como eu, você preferir uma funcionalidade mais "funcional" sem comprometer a velocidade, este exemplo usa a pesquisa rápida de dicionário dentro do fechamento de redução.
De acordo com este teste, minha solução é duas vezes mais rápida que a resposta proposta
fonte
fonte
Eu sei que meu código tem pouca duração e pouca complexidade de tempo, mas é compreensível, então tentei dessa maneira.
Estou tentando desenvolver uma função baseada em protótipo aqui e o código também muda.
Aqui, Distinct é minha própria função de protótipo.
fonte
Se você tiver Array.prototype.includes ou estiver disposto a preenchê- lo, isso funcionará:
fonte
Meu código abaixo mostrará a matriz única de idades, bem como a nova matriz que não possui idade duplicada
fonte
Eu escrevi o meu próprio em TypeScript, para um caso genérico, como o de Kotlin
Array.distinctBy {}
...Onde
U
é lavável, é claro. Para objetos, você pode precisar de https://www.npmjs.com/package/es6-json-stable-stringifyfonte
Caso você precise de um objeto inteiro
[Objeto {x: 1, y: 2}, Objeto {x: 2, y: 1}]
fonte
Responder a essa pergunta antiga é inútil, mas há uma resposta simples que fala da natureza do Javascript. Objetos em Javascript são inerentemente tabelas de hash. Podemos usar isso para obter um hash de chaves exclusivas:
Em seguida, podemos reduzir o hash para uma matriz de valores exclusivos:
Isso é tudo o que você precisa. A matriz a2 contém apenas as idades únicas.
fonte
Uma linha simples com ótimo desempenho. 6% mais rápido que as soluções ES6 nos meus testes .
fonte
array.map( o => o.age).filter( (v,i,a) => a.indexOf(v)===i)
. Eu uso a palavra-chave função tão raramente agora que eu tenho que ler coisas duas vezes quando eu vejo isso 😊