Como verifico se uma matriz inclui um valor em JavaScript?

3997

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.

Brad
fonte
49
acabou de testar: seu caminho é realmente o mais rápido para os navegadores: jsperf.com/find-element-in-obj-vs-array/2 (além de pré-salvar o comprimento da variável em uma variável) enquanto usa o indexOf (como em $ .inArray) é muito mais lento
Jörn Berkefeld
17
muitos responderam que a matriz # indexOf é sua melhor escolha aqui. Mas se você quiser algo que possa ser convertido corretamente em booleano, use o seguinte: ~[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.
Lordvlad #
8
~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 terminar return [1,2,3].indexOf(3) === -1; ~como binária, ela inverterá cada bit do valor individualmente.
Mcfedr
14
@Iordvlad [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 -1consiste em apenas 1's, é complemento 0, 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 com indexOf.
Mknecht 14/03/2015
5
O título é enganoso. Onde está a [[1,2],[3,4]].includes([3,4])?
Mplungjan #

Respostas:

4379

Navegadores modernos têm Array#includes, o que faz exatamente isso e é amplamente suportado por todos, exceto o IE:

console.log(['joe', 'jane', 'mary'].includes('jane')); //true

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:

Observe que algumas estruturas implementam isso como uma função, enquanto outras adicionam a função ao protótipo da matriz.

codeape
fonte
42
O MooTools também possui Array.contains que retorna um booleano, que soa como a verdadeira pergunta aqui.
Ryan Florence
22
protótipo também tem Array.includeque retorna um boolean
user102008
46
Se você estiver usando um bom navegador, você pode simplesmente usar:array.indexOf(object) != -1
Sam Soffes
13
Além disso, o uso não faça indexOf sozinho como uma condição, porque o primeiro elemento retornará 0 e será avaliada como Falsas
mais-
241
inArrayé um nome terrível para uma função que retorna o índice do elemento e, -1se ele não existir. Eu esperaria que um booleano fosse retornado.
Tim
434

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 whileloop decrescente é a maneira mais rápida de iterar em JavaScript. Portanto, você pode reescrever seu código da seguinte maneira:

function contains(a, obj) {
    var i = a.length;
    while (i--) {
       if (a[i] === obj) {
           return true;
       }
    }
    return false;
}

Obviamente, você também pode estender o protótipo de matriz:

Array.prototype.contains = function(obj) {
    var i = this.length;
    while (i--) {
        if (this[i] === obj) {
            return true;
        }
    }
    return false;
}

E agora você pode simplesmente usar o seguinte:

alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains('2')); // => false
Damir Zekić
fonte
22
"Comprovado" é uma palavra forte. Os mecanismos JS melhoram constantemente e o tempo de execução medido há 3 anos está terrivelmente desatualizado.
orip 20/11/11
2
@ Damir - eu concordo. Talvez altere a amostra para usar indexOf, se disponível, para que as pessoas que colem esse código cegamente obtenham o melhor desempenho possível.
Orip
1
@ cbmeeks sim, é definitivamente necessário cuidar. Foi provavelmente um caso de fazer for (o in array)o que não deve ser feito quando um loop através da matriz em geral ...
Damir Zekic
1
A melhor maneira de fazer isso é verificar se [1, 2, 3] .indexOf (1)> -1
Devin G Rhode
207

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:

[1, 2, 3].indexOf(1) => 0
["foo", "bar", "baz"].indexOf("bar") => 1
[1, 2, 3].indexOf(4) => -1

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 )

cic
fonte
na verdade, há um exemplo da implementação da extensão indexOf para navegadores que não a suportam na página developer.mozilla.org à qual você vinculou.
Lloyd Cotten
na verdade, se você adicionar o indexof ao protótipo de Array para navegadores que não o suportam (por exemplo, IE7), eles também tentarão fazer um loop sobre essa função ao percorrer os itens da matriz. desagradável.
CpILL
é aplicável verificar o objeto? eu não acho que funciona no caso do objeto
Himesh Aadeshara
169

O ECMAScript 7 apresenta Array.prototype.includes .

Pode ser usado assim:

[1, 2, 3].includes(2); // true
[1, 2, 3].includes(4); // false

Também aceita um segundo argumento opcional fromIndex:

[1, 2, 3].includes(3, 3); // false
[1, 2, 3].includes(3, -1); // true

Ao contrário indexOf, que usa Comparação de igualdade estrita , includescompara usando o algoritmo de igualdade SameValueZero . Isso significa que você pode detectar se uma matriz inclui um NaN:

[1, 2, NaN].includes(NaN); // true

Ao contrário indexOf, includestambém não ignora os índices ausentes:

new Array(5).includes(undefined); // true

Atualmente, ainda é um rascunho, mas pode ser preenchido com polyfill para fazê-lo funcionar em todos os navegadores.

Oriol
fonte
3
Não suportado para IE e Microsfot Edge (2015) ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… )
Adriano Resende
1
Também relevante, a tabela de compatibilidade do ES7 (parece que o chrome é compatível agora)
styfle
é aplicável verificar o objeto? eu não acho que funciona no caso do objeto
Himesh Aadeshara
128

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:

const items = [ {a: '1'}, {a: '2'}, {a: '3'} ]

items.some(item => item.a === '3')  // returns true
items.some(item => item.a === '4')  // returns false

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 ifdeclaração, pois retorna um booleano:

if (items.some(item => item.a === '3')) {
  // do something
}

* Como jamess apontou no comentário, no momento desta resposta, setembro de 2018, Array.prototype.some()é totalmente suportada: tabela de suporte do caniuse.com

Michael
fonte
1
A partir de hoje, em setembro de 2018, Array.prototype.some () é totalmente suportado: caniuse.com mesa de apoio
jamess
1
Trabalhando no nó> = 8.10 para o AWS Node.js Lambda, isso é ótimo. Solução muito limpa e simples! 👍🏻
Jordan
1
@jamess Pode ser bem suportado, mas lembre-se de que Arrow functionsneste exemplo não é tão bem suportado. Para obter mais detalhes, consulte aqui: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Kamil Witkowski
Algum curto-circuito? Ou itera toda a matriz, mesmo que tenha encontrado um valor?
Douglas Gaskell
@DouglasGaskell anula a iteração uma vez encontrada (mencionada na resposta) #
Michael #
112

Digamos que você definiu uma matriz assim:

const array = [1, 2, 3, 4]

Abaixo estão três maneiras de verificar se há um 3lá. Todos eles retornam trueou false.

Método de matriz nativa (desde ES2016) ( tabela de compatibilidade )

array.includes(3) // true

Como método Array personalizado (pré ES2016)

// Prefixing the method with '_' to avoid name clashes
Object.defineProperty(Array.prototype, '_includes', { value: function (v) { return this.indexOf(v) !== -1 }})
array._includes(3) // true

Função simples

const includes = (a, v) => a.indexOf(v) !== -1
includes(array, 3) // true
william malo
fonte
Ele retorna true se "b" é na matriz "a" ... Eu não sei outra forma de explicar isso ...
william malo
4
Esta parte eu não entendo "!! ~". E acho que isso não funcionará no IE8 porque o IE8 não suporta indexOf () no objeto Array.
svlada
62
"~" é um operador que pavimenta, inverte e subtrai 1 de um número. indexOf retorna -1 se falhar, então "~" transforma -1 em "0". usando "!!" voltas números em boleans (!! 0 === false)
william malo
1
Legal, mas sério, por uma questão de simplicidade, e não apenas a.indexOf (b)> - 1, já que "> -1" .length === "!! ~" .length
super
2
Eu chamaria a falta de conhecimento sobre os efeitos de operadores booleanos de pouco profissional. Mas eu concordo com o valor do código legível. Certamente colocaria isso em uma função claramente rotulada. E é exatamente isso que a maioria das principais estruturas JS faz.
precisa saber é
79

Aqui está uma implementação compatível com JavaScript 1.6 de Array.indexOf:

if (!Array.indexOf) {
    Array.indexOf = [].indexOf ?
        function(arr, obj, from) {
            return arr.indexOf(obj, from);
        } :
        function(arr, obj, from) { // (for IE6)
            var l = arr.length,
                i = from ? parseInt((1 * from) + (from < 0 ? l : 0), 10) : 0;
            i = i < 0 ? 0 : i;
            for (; i < l; i++) {
                if (i in arr && arr[i] === obj) {
                    return i;
                }
            }
            return -1;
        };
}
Már Örlygsson
fonte
Parece ótimo, mas um pouco confuso: * Os testes nas linhas 1 e 3 não são equivalentes? * Não seria melhor testar o protótipo e adicionar a função ao Array.prototype, se necessário?
Avi Flax
10
Eles não são equivalentes. [].indexOfé uma abreviação de Array.prototype.indexOf. Os programadores Javascript defensivos e paranóicos evitam estender protótipos nativos a todo custo.
Már Örlygsson
1
Não está [].indexOfcriando uma nova matriz e acessando indexOf, enquanto Array.prototype.indexOfapenas acessa o protótipo diretamente?
alex
3
@alex yes [].indexOf === Array.prototype.indexOf(experimente no FireBug), mas por outro lado [].indexOf !== Array.indexOf.
31511
57

Usar:

function isInArray(array, search)
{
    return array.indexOf(search) >= 0;
}

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}
Matías Cánepa
fonte
25
x ? true : falsegeralmente é redundante. Está aqui.
Ry-
@minitech Por que você diz que é redundante?
Matías Cánepa
8
array.indexOf(search) >= 0já é um booleano. Apenas return array.indexOf(search) >= 0.
Ry-
@minitech bem, obrigado! Na verdade, eu não sabia que essa construção poderia ser devolvida. Até algo novo.
Matías Cánepa 29/07
Literalmente, qualquer construção em javascript pode ser retornada
BT
49

Estender o Arrayobjeto JavaScript é uma péssima idéia, porque você introduz novas propriedades (seus métodos personalizados) em for-inloops 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.

Peter Mortensen
fonte
22
Discordo. Os loops de entrada não devem ser usados ​​para matrizes exatamente por esse motivo. Usando para-em loops quebrará quando se utiliza um dos js bibliotecas populares
Tomas
Isso seria considerado patch de macaco? Muitas pessoas gostam disso.
Cbmeeks 10/10/12
33

Uma linha:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}
AlonL
fonte
8
array.filter(e=>e==x).length > 0é equivalente a array.some(e=>e==x), mas someé mais eficiente
Apolo
28

Eu uso o seguinte:

Array.prototype.contains = function (v) {
    return this.indexOf(v) > -1;
}

var a = [ 'foo', 'bar' ];

a.contains('foo'); // true
a.contains('fox'); // false
Eduardo Cuomo
fonte
24
function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

Array.prototype.some () foi adicionado ao padrão ECMA-262 na 5ª edição

dansalmo
fonte
se estiver usando ES6 do que cam ser encurtar comocontains = (a, obj) => a.some((element) => element === obj))
diEcho
Até o IE9 tem suporte para Array.prototype.some () a partir do ECMAScript 5 .
precisa saber é o seguinte
19

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 ocontains 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 whilemais for, 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

function bidirectionalIndexOf(a, b, c, d, e){
  for(c=a.length,d=c*1; c--; ){
    if(a[c]==b) return c; //or this[c]===b
    if(a[e=d-1-c]==b) return e; //or a[e=d-1-c]===b
  }
  return -1
}

//Usage
bidirectionalIndexOf(array,'value');

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 containsfunção para refletir a saída indexOf & lastIndexOf (basicamente truecom os botões com indexe falsecom -1). Isso não deve prejudicá-lo.

A variante do protótipo de matriz

Object.defineProperty(Array.prototype,'bidirectionalIndexOf',{value:function(b,c,d,e){
  for(c=this.length,d=c*1; c--; ){
    if(this[c]==b) return c; //or this[c]===b
    if(this[e=d-1-c] == b) return e; //or this[e=d-1-c]===b
  }
  return -1
},writable:false, enumerable:false});

// Usage
array.bidirectionalIndexOf('value');

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 whilevariante:

function bidirectionalIndexOf(a, b, c, d){
  c=a.length; d=c-1;
  while(c--){
    if(b===a[c]) return c;
    if(b===a[d-c]) return d-c;
  }
  return c
}

// Usage
bidirectionalIndexOf(array,'value');

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

cocco
fonte
18

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

  • As soluções com base em JSON, Sete surpreendentemente find(K, N, S) são mais lento em todos os navegadores
  • o es6 includes(F) é rápido apenas no chrome
  • as soluções baseadas em for(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 eficiente
  • as soluções em que o índice diminui durante o loop, (B) é mais lento, provavelmente porque o modo de cache da CPU funciona .
  • Também executo teste para grande matriz quando o elemento pesquisado estava na posição 66% do comprimento da matriz, e as soluções baseadas em for(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

insira a descrição da imagem aqui

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.

Matriz pequena - 10 elementos

Você pode realizar testes em sua máquina AQUI

insira a descrição da imagem aqui

Matriz grande - 1.000.000 elementos

Você pode realizar testes em sua máquina AQUI

insira a descrição da imagem aqui

Kamil Kiełczewski
fonte
16

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.

Andru Luvisi
fonte
16
function inArray(elem,array)
{
    var len = array.length;
    for(var i = 0 ; i < len;i++)
    {
        if(array[i] == elem){return i;}
    }
    return -1;
} 

Retorna o índice da matriz, se encontrado, ou -1, se não encontrado

LmC
fonte
16

Usamos esse trecho (trabalha com objetos, matrizes, strings):

/*
 * @function
 * @name Object.prototype.inArray
 * @description Extend Object prototype within inArray function
 *
 * @param {mix}    needle       - Search-able needle
 * @param {bool}   searchInKey  - Search needle in keys?
 *
 */
Object.defineProperty(Object.prototype, 'inArray',{
    value: function(needle, searchInKey){

        var object = this;

        if( Object.prototype.toString.call(needle) === '[object Object]' || 
            Object.prototype.toString.call(needle) === '[object Array]'){
            needle = JSON.stringify(needle);
        }

        return Object.keys(object).some(function(key){

            var value = object[key];

            if( Object.prototype.toString.call(value) === '[object Object]' || 
                Object.prototype.toString.call(value) === '[object Array]'){
                value = JSON.stringify(value);
            }

            if(searchInKey){
                if(value === needle || key === needle){
                return true;
                }
            }else{
                if(value === needle){
                    return true;
                }
            }
        });
    },
    writable: true,
    configurable: true,
    enumerable: false
});

Uso:

var a = {one: "first", two: "second", foo: {three: "third"}};
a.inArray("first");          //true
a.inArray("foo");            //false
a.inArray("foo", true);      //true - search by keys
a.inArray({three: "third"}); //true

var b = ["one", "two", "three", "four", {foo: 'val'}];
b.inArray("one");         //true
b.inArray('foo');         //false
b.inArray({foo: 'val'})   //true
b.inArray("{foo: 'val'}") //false

var c = "String";
c.inArray("S");        //true
c.inArray("s");        //false
c.inArray("2", true);  //true
c.inArray("20", true); //false
dr.dimitru
fonte
15

Se você está verificando repetidamente a existência de um objeto em uma matriz, talvez deva procurar

  1. Mantendo a matriz sempre classificada, classificando a inserção em sua matriz (coloque novos objetos no lugar certo)
  2. Faça a atualização de objetos como remover + operação de inserção classificada e
  3. Use uma pesquisa de pesquisa binária no seu contains(a, obj).
Ztyx
fonte
2
Ou, se possível, pare de usar uma matriz inteiramente e, em vez disso, use um objeto como um dicionário, como sugeriram MattMcKnight e ninjagecko.
Joeytwiddle
13

Solução que funciona em todos os navegadores modernos:

function contains(arr, obj) {
  const stringifiedObj = JSON.stringify(obj); // Cache our object to not call `JSON.stringify` on every iteration
  return arr.some(item => JSON.stringify(item) === stringifiedObj);
}

Uso:

contains([{a: 1}, {a: 2}], {a: 1}); // true

Solução IE6 +:

function contains(arr, obj) {
  var stringifiedObj = JSON.stringify(obj)
  return arr.some(function (item) {
    return JSON.stringify(item) === stringifiedObj;
  });
}

// .some polyfill, not needed for IE9+
if (!('some' in Array.prototype)) {
  Array.prototype.some = function (tester, that /*opt*/) {
    for (var i = 0, n = this.length; i < n; i++) {
      if (i in this && tester.call(that, this[i], i, this)) return true;
    } return false;
  };
}

Uso:

contains([{a: 1}, {a: 2}], {a: 1}); // true

Por que usar JSON.stringify?

Array.indexOfe Array.includes(assim como a maioria das respostas aqui) são comparadas apenas por referência e não por valor.

[{a: 1}, {a: 2}].includes({a: 1});
// false, because {a: 1} is a new object

Bônus

Linha única ES6 não otimizada:

[{a: 1}, {a: 2}].some(item => JSON.stringify(item) === JSON.stringify({a: 1));
// true

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 containsfunção com uma otimização de desempenho. Obrigado itinance por apontá-lo.

Igor Barbashin
fonte
Esse pedaço de código específico pode funcionar no IE6 (não foi testado), mas o IE não suportava o ES5 até o IE9.
Mark Reed
Por razões de desempenho, você deve evitar as restrições. Pelo menos você deve evitar o JSON.stringify o "obj" em cada loop, porque é caro e desacelerará seu aplicativo. Portanto, você deve capturá-lo antes do loop for em uma variável temp
itinance
1
@itinance bom ponto. Atualizou a includesfunçã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.
Igor Barbashin
Nota tardia: isso não funciona, por exemplo, contains([{ a: 1, b: 2 }], { b: 2, a: 1 })porque os objetos com strings mantêm a ordem das propriedades.
Heretic Monkey
1
@HereticMonkey, é verdade. É por isso que adicionei a sort-keysnota na parte inferior
Igor Barbashin 16/10/19
12

Use 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:

$.inArray({'b': 2}, [{'a': 1}, {'b': 2}])
> -1

Minha recomendação:

_.some([{'a': 1}, {'b': 2}], {'b': 2})
> true

Notas:

$ .inArray funciona bem para determinar se existe um valor escalar em uma matriz de escalares ...

$.inArray(2, [1,2])
> 1

... 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:

(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)
l3x
fonte
10

O ECMAScript 6 tem uma proposta elegante em busca.

O método find executa a função de retorno de chamada uma vez para cada elemento presente na matriz até encontrar um em que o retorno de chamada retorne um valor verdadeiro. Se esse elemento for encontrado, find retornará imediatamente o valor desse elemento. Caso contrário, encontre retornos indefinidos. o retorno de chamada é invocado apenas para índices da matriz que atribuíram valores; não é chamado para índices que foram excluídos ou aos quais nunca foram atribuídos valores.

Aqui está a documentação MDN sobre isso.

A funcionalidade de localização funciona assim.

function isPrime(element, index, array) {
    var start = 2;
    while (start <= Math.sqrt(element)) {
        if (element % start++ < 1) return false;
    }
    return (element > 1);
}

console.log( [4, 6, 8, 12].find(isPrime) ); // Undefined, not found
console.log( [4, 5, 8, 12].find(isPrime) ); // 5

Você pode usar isso no ECMAScript 5 e abaixo, definindo a função .

if (!Array.prototype.find) {
  Object.defineProperty(Array.prototype, 'find', {
    enumerable: false,
    configurable: true,
    writable: true,
    value: function(predicate) {
      if (this == null) {
        throw new TypeError('Array.prototype.find called on null or undefined');
      }
      if (typeof predicate !== 'function') {
        throw new TypeError('predicate must be a function');
      }
      var list = Object(this);
      var length = list.length >>> 0;
      var thisArg = arguments[1];
      var value;

      for (var i = 0; i < length; i++) {
        if (i in list) {
          value = list[i];
          if (predicate.call(thisArg, value, i, list)) {
            return value;
          }
        }
      }
      return undefined;
    }
  });
}
Pradeep Mahdevu
fonte
9

Embora array.indexOf(x)!=-1seja 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ça table[x]!==undefinedou ===undefined:

Array.prototype.toTable = function() {
    var t = {};
    this.forEach(function(x){t[x]=true});
    return t;
}

Demo:

var toRemove = [2,4].toTable();
[1,2,3,4,5].filter(function(x){return toRemove[x]===undefined})

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

ninjagecko
fonte
9

Pode-se usar Set que possui o método "has ()":

function contains(arr, obj) {
      var proxy = new Set(arr);
      if (proxy.has(obj))
        return true;
      else
        return false;
    }

    var arr = ['Happy', 'New', 'Year'];
    console.log(contains(arr, 'Happy'));

rlib
fonte
5
Eu acho que return proxy.has(obj)é muito mais limpo do que duas linhas com if-else aqui
Maciej Bukowski
function contains(arr, obj) { return new Set(arr).has(obj); }
Gordon Bean
8

Usar:

var myArray = ['yellow', 'orange', 'red'] ;

alert(!!~myArray.indexOf('red')); //true

Demo

Para saber exatamente o que tilde ~fazer neste momento, consulte esta pergunta O que um til faz quando precede uma expressão? .

Mina Gabriel
fonte
5
Isso já foi publicado ano e meio atrás, sem necessidade de repeti-lo.
Shadow Wizard é Ear For You
3
Na verdade, não foi publicado. Não como resposta, mas como comentário a uma resposta, e mesmo assim não é claro e conciso. Obrigado por postar, Mina Gabriel.
T.CK
6

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:

function contains(a, obj) {
    for (var i = 0; i < a.length; i++) {
        if (JSON.stringify(a[i]) === JSON.stringify(obj)) {
            return true;
        }
    }
    return false;
}
Alireza
fonte
Nota tardia: isso não funciona, por exemplo, contains([{ a: 1, b: 2 }], { b: 2, a: 1 })porque os objetos com strings mantêm a ordem das propriedades.
Heretic Monkey
5

De maneira alguma o melhor, mas eu estava apenas sendo criativo e aumentando o repertório.

Não use isso

Object.defineProperty(Array.prototype, 'exists', {
  value: function(element, index) {

    var index = index || 0

    return index === this.length ? -1 : this[index] === element ? index : this.exists(element, ++index)
  }
})


// Outputs 1
console.log(['one', 'two'].exists('two'));

// Outputs -1
console.log(['one', 'two'].exists('three'));

console.log(['one', 'two', 'three', 'four'].exists('four'));

sqram
fonte
O que você deve usar se não for esse?
bryc
@bryc talvez a solução aceita, ou outra solução daqui. Se você não se importam muito para o desempenho, do que você pode usar este
sqram
5

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.

let obj = { name: 'Sumer', age: 36 };
let arrObj = [obj, { name: 'Kishor', age: 46 }, { name: 'Rupen', age: 26 }];


console.log(arrObj.indexOf(obj));// 0
console.log(arrObj.indexOf({ name: 'Sumer', age: 36 })); //-1

console.log([1, 3, 5, 2].indexOf(2)); //3

Array.prototype. inclui -> (retorna verdadeiro ou falso )

console.log(arrObj.includes(obj));  //true
console.log(arrObj.includes({ name: 'Sumer', age: 36 })); //false

console.log([1, 3, 5, 2].includes(2)); //true

Array.prototype. find -> (recebe retorno de chamada, retorna o primeiro valor / objeto que retorna true no CB).

console.log(arrObj.find(e => e.age > 40));  //{ name: 'Kishor', age: 46 }
console.log(arrObj.find(e => e.age > 40)); //{ name: 'Kishor', age: 46 }

console.log([1, 3, 5, 2].find(e => e > 2)); //3

Array.prototype. findIndex -> (recebe retorno de chamada, retorna o índice do primeiro valor / objeto que retorna verdadeiro no CB).

console.log(arrObj.findIndex(e => e.age > 40));  //1
console.log(arrObj.findIndex(e => e.age > 40)); //1

console.log([1, 3, 5, 2].findIndex(e => e > 2)); //1

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.

verão
fonte
5

A solução simples para esse requisito é usar find()

Se você estiver tendo uma variedade de objetos como abaixo,

var users = [{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "admin"},
{id: "105", name: "user"}];

Então você pode verificar se o objeto com seu valor já está presente ou não

let data = users.find(object => object['id'] === '104');

se os dados forem nulos, não haverá administrador; caso contrário, ele retornará o objeto existente, como abaixo.

{id: "104", name: "admin"}

Em seguida, você pode encontrar o índice desse objeto na matriz e substituí-lo usando o código abaixo.

let indexToUpdate = users.indexOf(data);
let newObject = {id: "104", name: "customer"};
users[indexToUpdate] = newObject;//your new object
console.log(users);

você terá valor como abaixo

[{id: "101", name: "Choose one..."},
{id: "102", name: "shilpa"},
{id: "103", name: "anita"},
{id: "104", name: "customer"},
{id: "105", name: "user"}];

espero que isso ajude alguém.

Shiva
fonte
5

    function countArray(originalArray) {
     
    	var compressed = [];
    	// make a copy of the input array
    	var copyArray = originalArray.slice(0);
     
    	// first loop goes over every element
    	for (var i = 0; i < originalArray.length; i++) {
     
    		var count = 0;	
    		// loop over every element in the copy and see if it's the same
    		for (var w = 0; w < copyArray.length; w++) {
    			if (originalArray[i] == copyArray[w]) {
    				// increase amount of times duplicate is found
    				count++;
    				// sets item to undefined
    				delete copyArray[w];
    			}
    		}
     
    		if (count > 0) {
    			var a = new Object();
    			a.value = originalArray[i];
    			a.count = count;
    			compressed.push(a);
    		}
    	}
     
    	return compressed;
    };
    
    // It should go something like this:
    
    var testArray = new Array("dog", "dog", "cat", "buffalo", "wolf", "cat", "tiger", "cat");
    var newArray = countArray(testArray);
    console.log(newArray);

Sanjay Magar
fonte