Qual é a maneira mais concisa e eficiente de descobrir se uma matriz JavaScript contém um valor?
Esta é a única maneira que sei fazer:
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
Existe uma maneira melhor e mais concisa de fazer isso?
Isso está muito relacionado à pergunta Stack Overflow. Melhor maneira de encontrar um item em uma matriz JavaScript? que aborda a localização de objetos em uma matriz usando indexOf
.
~[1,2,3].indexOf(4)
retornará 0, que será avaliado como falso, enquanto~[1,2,3].indexOf(3)
retornará -3, que será avaliado como verdadeiro.~
não é o que você deseja usar para converter em um booleano, para o que você precisa!
. Mas, neste caso, você deseja verificar a igualdade com -1, para que a função possa terminarreturn [1,2,3].indexOf(3) === -1;
~
como binária, ela inverterá cada bit do valor individualmente.[1,2,3].indexOf(4)
irá realmente retornar -1 . Como @mcfedr apontou,~
é o operador NOT bit a bit , consulte ES5 11.4.8. O fato é que, como a representação binária de-1
consiste em apenas 1's, é complemento0
, que é avaliado como falso. O complemento de qualquer outro número será diferente de zero, portanto verdadeiro. Portanto,~
funciona muito bem e é frequentemente usado em conjunto comindexOf
.[[1,2],[3,4]].includes([3,4])
?Respostas:
Navegadores modernos têm
Array#includes
, o que faz exatamente isso e é amplamente suportado por todos, exceto o IE:Você também pode usar
Array#indexOf
, que é menos direto, mas não requer polyfills para navegadores desatualizados.Muitas estruturas também oferecem métodos semelhantes:
$.inArray(value, array, [fromIndex])
_.contains(array, value)
(também com alias como_.include
e_.includes
)dojo.indexOf(array, value, [fromIndex, findLast])
array.indexOf(value)
array.indexOf(value)
findValue(array, value)
array.indexOf(value)
Ext.Array.contains(array, value)
_.includes(array, value, [from])
(é_.contains
4.0.0 anterior)R.includes(value, array)
Observe que algumas estruturas implementam isso como uma função, enquanto outras adicionam a função ao protótipo da matriz.
fonte
Array.include
que retorna um booleanarray.indexOf(object) != -1
inArray
é um nome terrível para uma função que retorna o índice do elemento e,-1
se ele não existir. Eu esperaria que um booleano fosse retornado.Atualização de 2019: esta resposta é de 2008 (11 anos!) E não é relevante para o uso moderno de JS. A melhoria de desempenho prometida foi baseada em uma referência feita nos navegadores da época. Pode não ser relevante para os contextos modernos de execução de JS. Se você precisar de uma solução fácil, procure outras respostas. Se você precisar do melhor desempenho, faça um benchmark para si nos ambientes de execução relevantes.
Como já foi dito, a iteração na matriz é provavelmente a melhor maneira, mas foi provado que um
while
loop decrescente é a maneira mais rápida de iterar em JavaScript. Portanto, você pode reescrever seu código da seguinte maneira:Obviamente, você também pode estender o protótipo de matriz:
E agora você pode simplesmente usar o seguinte:
fonte
for (o in array)
o que não deve ser feito quando um loop através da matriz em geral ...indexOf
talvez, mas é uma "extensão JavaScript para o padrão ECMA-262; portanto, pode não estar presente em outras implementações do padrão".Exemplo:
AFAICS A Microsoft não oferece algum tipo de alternativa a isso, mas você pode adicionar funcionalidades semelhantes às matrizes no Internet Explorer (e outros navegadores que não suportam
indexOf
) se desejar, como revela uma pesquisa rápida no Google (por exemplo, esta )fonte
O ECMAScript 7 apresenta
Array.prototype.includes
.Pode ser usado assim:
Também aceita um segundo argumento opcional
fromIndex
:Ao contrário
indexOf
, que usa Comparação de igualdade estrita ,includes
compara usando o algoritmo de igualdade SameValueZero . Isso significa que você pode detectar se uma matriz inclui umNaN
:Ao contrário
indexOf
,includes
também não ignora os índices ausentes:Atualmente, ainda é um rascunho, mas pode ser preenchido com polyfill para fazê-lo funcionar em todos os navegadores.
fonte
As respostas principais assumem tipos primitivos, mas se você deseja descobrir se uma matriz contém um objeto com alguma característica, Array.prototype.some () é uma solução muito elegante:
O bom disso é que a iteração é abortada quando o elemento é encontrado, para que ciclos de iteração desnecessários sejam poupados.
Além disso, ele se encaixa perfeitamente em uma
if
declaração, pois retorna um booleano:* Como jamess apontou no comentário, no momento desta resposta, setembro de 2018,
Array.prototype.some()
é totalmente suportada: tabela de suporte do caniuse.comfonte
Arrow functions
neste exemplo não é tão bem suportado. Para obter mais detalhes, consulte aqui: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…Digamos que você definiu uma matriz assim:
Abaixo estão três maneiras de verificar se há um
3
lá. Todos eles retornamtrue
oufalse
.Método de matriz nativa (desde ES2016) ( tabela de compatibilidade )
Como método Array personalizado (pré ES2016)
Função simples
fonte
Aqui está uma implementação compatível com JavaScript 1.6 de
Array.indexOf
:fonte
[].indexOf
é uma abreviação deArray.prototype.indexOf
. Os programadores Javascript defensivos e paranóicos evitam estender protótipos nativos a todo custo.[].indexOf
criando uma nova matriz e acessandoindexOf
, enquantoArray.prototype.indexOf
apenas acessa o protótipo diretamente?[].indexOf === Array.prototype.indexOf
(experimente no FireBug), mas por outro lado[].indexOf !== Array.indexOf
.Usar:
fonte
x ? true : false
geralmente é redundante. Está aqui.array.indexOf(search) >= 0
já é um booleano. Apenasreturn array.indexOf(search) >= 0
.Estender o
Array
objeto JavaScript é uma péssima idéia, porque você introduz novas propriedades (seus métodos personalizados) emfor-in
loops que podem quebrar os scripts existentes. Alguns anos atrás, os autores da biblioteca Prototype tiveram que reprojetar a implementação da biblioteca para remover esse tipo de coisa.Se você não precisa se preocupar com a compatibilidade com outro JavaScript em execução na sua página, vá em frente, caso contrário, recomendo a solução de função independente mais estranha, mas mais segura.
fonte
Uma linha:
fonte
array.filter(e=>e==x).length > 0
é equivalente aarray.some(e=>e==x)
, massome
é mais eficientePensando fora da caixa por um segundo, se você estiver fazendo essa ligação muitas vezes, é muito mais eficiente usar
uma matriz associativa deum mapa para fazer pesquisas usando uma função hash.https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
fonte
Eu uso o seguinte:
fonte
Array.prototype.some () foi adicionado ao padrão ECMA-262 na 5ª edição
fonte
contains = (a, obj) => a.some((element) => element === obj))
Uma alternativa bidirecional
indexOf
/ esperançosamente mais rápidalastIndexOf
2015
Embora o novo método inclua é muito bom, o suporte é basicamente zero por enquanto.
Há muito tempo que eu estava pensando em uma maneira de substituir as funções indexOf / lastIndexOf lentas.
Uma maneira de desempenho já foi encontrada, observando as principais respostas. Daqueles eu escolhi o
contains
função postada por @Damir Zekic, que deve ser a mais rápida. Mas também afirma que os benchmarks são de 2008 e estão desatualizados.Eu também prefiro
while
maisfor
, mas por não um motivo específico Acabei escrevendo a função com um loop for. Também poderia ser feito com umwhile --
.Fiquei curioso se a iteração era muito mais lenta se eu verificasse os dois lados da matriz enquanto fazia isso. Aparentemente não, e, portanto, essa função é cerca de duas vezes mais rápida do que as votadas com maior número de votos. Obviamente, também é mais rápido que o nativo. Isso em um ambiente do mundo real, onde você nunca sabe se o valor que está pesquisando está no início ou no final da matriz.
Quando você sabe que acabou de empurrar uma matriz com um valor, usar lastIndexOf provavelmente é a melhor solução, mas se você precisar percorrer grandes matrizes e o resultado puder estar em qualquer lugar, essa poderá ser uma solução sólida para tornar as coisas mais rápidas.
Índice bidirecionalOf / lastIndexOf
Teste de performance
http://jsperf.com/bidirectionalindexof
Como teste, criei uma matriz com 100 mil entradas.
Três consultas: no início, no meio e no final da matriz.
Espero que você também ache isso interessante e teste o desempenho.
Nota: Como você pode ver, modifiquei levemente a
contains
função para refletir a saída indexOf & lastIndexOf (basicamentetrue
com os botões comindex
efalse
com-1
). Isso não deve prejudicá-lo.A variante do protótipo de matriz
A função também pode ser facilmente modificada para retornar verdadeiro ou falso, ou mesmo o objeto, a string ou o que for.
E aqui está a
while
variante:Como isso é possível?
Eu acho que o cálculo simples para obter o índice refletido em uma matriz é tão simples que é duas vezes mais rápido que fazer uma iteração de loop real.
Aqui está um exemplo complexo que faz três verificações por iteração, mas isso só é possível com um cálculo mais longo que causa a lentidão do código.
http://jsperf.com/bidirectionalindexof/2
fonte
atuação
Hoje 2020.01.07 realizo testes no MacOs HighSierra 10.13.6 no Chrome v78.0.0, Safari v13.0.4 e Firefox v71.0.0 para 15 soluções escolhidas. Conclusões
JSON
,Set
e surpreendentementefind
(K, N, S) são mais lento em todos os navegadoresincludes
(F) é rápido apenas no chromefor
(C, D) eindexOf
(G, H) são bastante rápidas em todos os navegadores em matrizes pequenas e grandes; portanto, provavelmente são a melhor escolha para uma solução eficientefor
(C, D, E) dão resultados semelhantes (~ 630 ops / s - mas o E no safari e no firefox era 10- 20% mais lento que C e D)Resultados
Detalhes
Realizo 2 casos de teste: para matriz com 10 elementos e matriz com 1 milhão de elementos. Nos dois casos, colocamos o elemento pesquisado no meio da matriz.
Mostrar snippet de código
Matriz pequena - 10 elementos
Você pode realizar testes em sua máquina AQUI
Matriz grande - 1.000.000 elementos
Você pode realizar testes em sua máquina AQUI
fonte
Se você estiver usando JavaScript 1.6 ou posterior (Firefox 1.5 ou posterior), poderá usar Array.indexOf . Caso contrário, acho que você terminará com algo semelhante ao seu código original.
fonte
Retorna o índice da matriz, se encontrado, ou -1, se não encontrado
fonte
Usamos esse trecho (trabalha com objetos, matrizes, strings):
Uso:
fonte
Se você está verificando repetidamente a existência de um objeto em uma matriz, talvez deva procurar
contains(a, obj)
.fonte
Solução que funciona em todos os navegadores modernos:
Uso:
Solução IE6 +:
Uso:
Por que usar
JSON.stringify
?Array.indexOf
eArray.includes
(assim como a maioria das respostas aqui) são comparadas apenas por referência e não por valor.Bônus
Linha única ES6 não otimizada:
Nota: A comparação de objetos por valor funcionará melhor se as chaves estiverem na mesma ordem. Portanto, para garantir a segurança, você deve classificar as chaves primeiro com um pacote como este: https://www.npmjs.com/package/sort-keys
Atualizada a
contains
função com uma otimização de desempenho. Obrigado itinance por apontá-lo.fonte
includes
função com sua sugestão. Eu executei jsperf com minha função. É cerca de 5x mais lento que o lodash's inclui. Embora lodash não se compara em valor e não pode encontrar{a: 1}
em[{a: 1}]
. Não sei se alguma biblioteca faz isso. Mas estou curioso para saber se existe alguma maneira mais eficiente e não insanamente complexa de fazer isso.contains([{ a: 1, b: 2 }], { b: 2, a: 1 })
porque os objetos com strings mantêm a ordem das propriedades.sort-keys
nota na parte inferiorUse alguma função do lodash .
É conciso, preciso e possui excelente suporte entre plataformas.
A resposta aceita nem atende aos requisitos.
Requisitos: recomende a maneira mais concisa e eficiente de descobrir se uma matriz JavaScript contém um objeto.
Resposta Aceita:
Minha recomendação:
Notas:
$ .inArray funciona bem para determinar se existe um valor escalar em uma matriz de escalares ...
... mas a pergunta claramente pede uma maneira eficiente de determinar se um objeto está contido em uma matriz.
Para lidar com escalares e objetos, você pode fazer o seguinte:
fonte
O ECMAScript 6 tem uma proposta elegante em busca.
Aqui está a documentação MDN sobre isso.
A funcionalidade de localização funciona assim.
Você pode usar isso no ECMAScript 5 e abaixo, definindo a função .
fonte
Embora
array.indexOf(x)!=-1
seja a maneira mais concisa de fazer isso (e seja suportada por navegadores que não sejam o Internet Explorer há mais de uma década ...), não é O (1), mas O (N), o que é terrível. Se sua matriz não for alterada, você poderá convertê-la em uma hashtable e, em seguida, façatable[x]!==undefined
ou===undefined
:Demo:
(Infelizmente, embora você possa criar um Array.prototype.contains para "congelar" uma matriz e armazenar uma hashtable nesta._cache em duas linhas, isso geraria resultados incorretos se você optar por editar sua matriz mais tarde. O JavaScript possui ganchos insuficientes para mantenha esse estado, diferente do Python, por exemplo.)
fonte
Pode-se usar Set que possui o método "has ()":
fonte
return proxy.has(obj)
é muito mais limpo do que duas linhas com if-else aquifunction contains(arr, obj) { return new Set(arr).has(obj); }
Usar:
Demo
Para saber exatamente o que
tilde
~
fazer neste momento, consulte esta pergunta O que um til faz quando precede uma expressão? .fonte
OK, você pode otimizar seu código para obter o resultado!
Existem muitas maneiras de fazer isso que são mais limpas e melhores, mas eu só queria que seu padrão se aplicasse a isso
JSON.stringify
, basta fazer algo assim no seu caso:fonte
contains([{ a: 1, b: 2 }], { b: 2, a: 1 })
porque os objetos com strings mantêm a ordem das propriedades.De maneira alguma o melhor, mas eu estava apenas sendo criativo e aumentando o repertório.
Não use isso
fonte
Surpreso que esta pergunta ainda não tenha a sintaxe mais recente adicionada, adicionando meus 2 centavos.
Digamos que temos uma matriz de objetos arrObj e queremos pesquisar obj nele.
Array.prototype. indexOf -> (retorna índice ou -1 ) geralmente é usado para encontrar o índice do elemento na matriz. Isso também pode ser usado para pesquisar objetos, mas só funciona se você estiver passando referência ao mesmo objeto.
Array.prototype. inclui -> (retorna verdadeiro ou falso )
Array.prototype. find -> (recebe retorno de chamada, retorna o primeiro valor / objeto que retorna true no CB).
Array.prototype. findIndex -> (recebe retorno de chamada, retorna o índice do primeiro valor / objeto que retorna verdadeiro no CB).
Como find e findIndex recebem um retorno de chamada, podemos buscar qualquer objeto (mesmo que não tenhamos a referência) da matriz, configurando de forma criativa a verdadeira condição.
fonte
A solução simples para esse requisito é usar
find()
Se você estiver tendo uma variedade de objetos como abaixo,
Então você pode verificar se o objeto com seu valor já está presente ou não
se os dados forem nulos, não haverá administrador; caso contrário, ele retornará o objeto existente, como abaixo.
Em seguida, você pode encontrar o índice desse objeto na matriz e substituí-lo usando o código abaixo.
você terá valor como abaixo
espero que isso ajude alguém.
fonte
fonte