valor - percorrer array de objetos javascript




Como faço para verificar se uma matriz inclui um objeto em JavaScript? (20)

Qual é a maneira mais concisa e eficiente de descobrir se uma matriz JavaScript contém um objeto?

Essa é 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 conseguir isso?

Isso está intimamente relacionado à pergunta do Stack Overflow. A melhor maneira de encontrar um item em uma matriz JavaScript? que aborda a localização de objetos em uma matriz usando indexOf .


Aqui está uma implementação compatível com JavaScript 1.6 do 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;
      };
}

As respostas principais assumem tipos primitivos, mas se você quiser descobrir se uma matriz contém um objeto com alguma característica, o 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, de forma que os ciclos de iteração desnecessários são salvos.

Além disso, cabe muito bem em uma instrução if uma vez que retorna um booleano:

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

* Como a jamess apontou no comentário, a partir de hoje, setembro de 2018, o Array.prototype.some() é totalmente suportado: caniuse.com support table


Estender o objeto JavaScript Array é uma péssima ideia porque você introduz novas propriedades (seus métodos customizados) for-in loops for-in que podem quebrar scripts existentes. Há alguns anos, os autores da biblioteca Prototype tiveram que reprojetar a implementação da biblioteca para remover apenas 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, eu recomendaria a solução de função independente mais desajeitada, porém mais segura.


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

O ECMAScript 7 apresenta o array.includes(value) .

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 de indexOf , que usa comparação de igualdade estrita , includes comparações usando o algoritmo de igualdade SameValueZero . Isso significa que você pode detectar se uma matriz inclui um NaN :

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

Além disso, ao contrário de indexOf , includes não ignora os índices ausentes:

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

Atualmente, ainda é um rascunho, mas pode ser polyfill para que funcione em todos os navegadores.


One-liner:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}


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

  1. Mantendo o array ordenado todo o tempo fazendo ordenação por inserção em seu array (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) .

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 o JSON.stringify ?

Array.indexOf e Array.includes (assim como a maioria das respostas aqui) só comparam 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

Non-optimized ES6 one-liner:

[{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, por segurança, você pode classificar as chaves primeiro com um pacote como este: https://www.npmjs.com/package/sort-keys

Atualizada a função contains com uma otimização perf. Obrigado por indicar isso.


Usamos esse trecho (funciona com objetos, matrizes, cadeias de caracteres):

/*
 * @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

Usar:

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

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}


Veja como o Prototype faz isso :

/**
 *  Array#indexOf(item[, offset = 0]) -> Number
 *  - item (?): A value that may or may not be in the array.
 *  - offset (Number): The number of initial items to skip before beginning the
 *      search.
 *
 *  Returns the position of the first occurrence of `item` within the array &mdash; or
 *  `-1` if `item` doesn't exist in the array.
**/
function indexOf(item, i) {
  i || (i = 0);
  var length = this.length;
  if (i < 0) i = length + i;
  for (; i < length; i++)
    if (this[i] === item) return i;
  return -1;
}

Veja também here como eles se conectam.


indexOf talvez, mas é uma "extensão JavaScript para o padrão ECMA-262; como tal, 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 para isso, mas você pode adicionar funcionalidade semelhante a matrizes no Internet Explorer (e outros navegadores que não suportam indexOf ) se você quiser, como revela uma rápida pesquisa no Google (por exemplo, um ).


Atualização: Como @orip menciona nos comentários, o benchmark vinculado foi feito em 2008, então os resultados podem não ser relevantes para os navegadores modernos. No entanto, você provavelmente precisará disso para dar suporte a navegadores não modernos e, provavelmente, eles não foram atualizados desde então. Sempre teste você mesmo.

Como outros disseram, a iteração através da matriz é provavelmente a melhor maneira, mas foi provado que um loop while decrescente é a maneira mais rápida de iterar em JavaScript. Então você pode querer reescrever seu código da seguinte forma:

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

Claro, você também pode estender o protótipo de array:

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

Uma alternativa esperançosamente mais rápida bidirecional indexOf / lastIndexOf

2015

Enquanto o novo método array.includes(value) é muito bom, o suporte é basicamente zero por enquanto.

Há muito tempo que eu estava pensando em substituir as funções slow index / lastIndexOf.

Um modo de desempenho já foi encontrado, olhando para as respostas principais. Daqueles que escolhi, a função contains postada por @Damir Zekic, que deveria ser a mais rápida. Mas também afirma que os benchmarks são de 2008 e, portanto, estão desatualizados.

Eu também prefiro while longo for , mas por não uma razão específica acabei de escrever a função com um loop for. Também poderia ser feito com um while -- .

Fiquei curioso se a iteração fosse muito mais lenta se eu verificasse os dois lados do array enquanto fazia isso. Aparentemente não, e assim esta função é cerca de duas vezes mais rápida que as votadas. Obviamente, também é mais rápido que o nativo. Isso em um ambiente do mundo real, em que você nunca sabe se o valor que está procurando está no início ou no final do array.

Quando você sabe que apenas empurrou um array com um valor, usar lastIndexOf provavelmente será a melhor solução, mas se você tiver que viajar por grandes matrizes e o resultado puder estar em todos os lugares, isso pode ser uma solução sólida para tornar as coisas mais rápidas.

Bidirecional indexOf / 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 um array com 100k entradas.

Três perguntas: no início, no meio e no final do array.

Espero que você também ache isso interessante e teste o desempenho.

Nota: Como você pode ver, modifiquei levemente a função contains para refletir a saída indexOf & lastIndexOf (então, basicamente, true com o index e false com -1 ). Isso não deveria 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 true ou false ou até mesmo o objeto, string ou seja o que for.

E aqui está a variante while :

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 do que fazer uma iteração de loop real.

Aqui está um exemplo complexo fazendo 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


Você também pode usar este truque:

var arrayContains = function(object) {
  return (serverList.filter(function(currentObject) {
    if (currentObject === object) {
      return currentObject
    }
    else {
      return false;
    }
  }).length > 0) ? true : false
}

Como outros já mencionaram, você pode usar Array.indexOf, mas não está disponível em todos os navegadores. Aqui está o código de https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/indexOf para que ele funcione da mesma forma em navegadores mais antigos.

indexOf é uma adição recente ao padrão ECMA-262; como tal, pode não estar presente em todos os navegadores.Você pode contornar isso inserindo o seguinte código no início de seus scripts, permitindo o uso de indexOf em implementações que não o suportam nativamente. Este algoritmo é exatamente aquele especificado no ECMA-262, 5ª edição, assumindo que Object, TypeError, Number, Math.floor, Math.abs e Math.max tenham seu valor original.

if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {
        "use strict";
        if (this == null) {
            throw new TypeError();
        }
        var t = Object(this);
        var len = t.length >>> 0;
        if (len === 0) {
            return -1;
        }
        var n = 0;
        if (arguments.length > 1) {
            n = Number(arguments[1]);
            if (n != n) { // shortcut for verifying if it's NaN
                n = 0;
            } else if (n != 0 && n != Infinity && n != -Infinity) {
                n = (n > 0 || -1) * Math.floor(Math.abs(n));
            }
        }
        if (n >= len) {
            return -1;
        }
        var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);
        for (; k < len; k++) {
            if (k in t && t[k] === searchElement) {
                return k;
            }
        }
        return -1;
    }
}

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

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





browser