recorrer - saber si un elemento existe en un array javascript




¿Cómo verifico si una matriz incluye un objeto en JavaScript? (20)

¿Cuál es la forma más concisa y eficiente de averiguar si una matriz de JavaScript contiene un objeto?

Esta es la única manera que conozco para hacerlo:

function contains(a, obj) {
    for (var i = 0; i < a.length; i++) {
        if (a[i] === obj) {
            return true;
        }
    }
    return false;
}

¿Hay una manera mejor y más concisa para lograr esto?

Esto está muy relacionado con la pregunta de desbordamiento de pila ¿La mejor forma de encontrar un elemento en una matriz de JavaScript? que aborda la búsqueda de objetos en una matriz utilizando indexOf .


Aquí hay una implementación compatible con 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;
      };
}

Así es como lo hace Prototype :

/**
 *  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;
}

También vea here cómo se enganchan.


De acuerdo, ¡solo puede optimizar su código para obtener el resultado! Hay muchas maneras de hacer esto que son más limpias y mejores, pero solo quería obtener su patrón y aplicar a eso usando JSON.stringify , simplemente haga algo como esto en su 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;
}

ECMAScript 6 tiene una propuesta elegante en encontrar.

El método de búsqueda ejecuta la función de devolución de llamada una vez para cada elemento presente en la matriz hasta que encuentra uno donde la devolución de llamada devuelve un valor verdadero. Si se encuentra un elemento de este tipo, find devuelve inmediatamente el valor de ese elemento. De lo contrario, encontrar devuelve indefinido. la devolución de llamada se invoca solo para los índices de la matriz que tienen valores asignados; no se invoca para los índices que se han eliminado o que nunca se han asignado valores.

Aquí está la documentación de MDN sobre eso.

La funcionalidad de búsqueda funciona así.

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

Puede usar esto en ECMAScript 5 y más abajo definiendo la función .

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

Extender el objeto de la Array JavaScript es una muy mala idea porque introduce nuevas propiedades (sus métodos personalizados) for-in bucles for-in que pueden romper los scripts existentes. Hace unos años, los autores de la biblioteca Prototype tuvieron que rediseñar la implementación de su biblioteca para eliminar este tipo de cosas.

Si no necesita preocuparse por la compatibilidad con otro JavaScript que se ejecuta en su página, hágalo, de lo contrario, le recomendaría la solución de función independiente más incómoda pero más segura.


Las respuestas principales asumen tipos primitivos, pero si desea averiguar si una matriz contiene un objeto con algún rasgo, Array.prototype.some() es una solución muy 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

Lo bueno de esto es que la iteración se cancela una vez que se encuentra el elemento, por lo que se guardan ciclos de iteración innecesarios.

Además, encaja muy bien en una sentencia if , ya que devuelve un booleano:

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

* Como señala Jamess en el comentario, a partir de hoy, septiembre de 2018, Array.prototype.some() es totalmente compatible: tabla de soporte de caniuse.com



Si bien array.indexOf(x)!=-1 es la forma más concisa de hacerlo (y ha sido compatible con navegadores que no son de Internet Explorer durante más de una década ...), no es O (1), sino O (1) N), que es terrible. Si su matriz no cambiará, puede convertirla a una tabla hash, luego haga la table[x]!==undefined o ===undefined :

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

Manifestación:

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

(Desafortunadamente, aunque puede crear un Array.prototype.contains para "congelar" una matriz y almacenar una tabla hash en this._cache en dos líneas, esto daría resultados incorrectos si eligiera editar su matriz más adelante. JavaScript no tiene suficientes enlaces para te permite mantener este estado, a diferencia de Python, por ejemplo.)


Si está utilizando JavaScript 1.6 o posterior (Firefox 1.5 o posterior) puede usar Array.indexOf . De lo contrario, creo que vas a terminar con algo similar a tu código original.


Solución que funciona en todos los 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

Solución 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 qué usar JSON.stringify ?

Array.indexOf y Array.includes (así como la mayoría de las respuestas aquí) solo se comparan por referencia y no por valor.

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

Prima

ES6 no optimizado de una sola línea:

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

Nota: la comparación de objetos por valor funcionará mejor si las claves están en el mismo orden, por lo que, para estar seguro, puede ordenarlas primero con un paquete como este: https://www.npmjs.com/package/sort-keys

Actualización de la función contains con una optimización de perf. Gracias itinance por señalarlo.


Un trazador de líneas:

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

Uno puede usar el Set que tiene el 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'));

Usamos este fragmento de código (funciona con objetos, matrices, cadenas):

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

Utilizar:

Array.prototype.contains = function(x){
  var retVal = -1;

  // x is a primitive type
  if(["string","number"].indexOf(typeof x)>=0 ){ retVal = this.indexOf(x);}

  // x is a function
  else if(typeof x =="function") for(var ix in this){
    if((this[ix]+"")==(x+"")) retVal = ix;
  }

  //x is an object...
  else {
    var sx=JSON.stringify(x);
    for(var ix in this){
      if(typeof this[ix] =="object" && JSON.stringify(this[ix])==sx) retVal = ix;
    }
  }

  //Return False if -1 else number if numeric otherwise string
  return (retVal === -1)?false : ( isNaN(+retVal) ? retVal : +retVal);
}

Sé que no es la mejor manera de hacerlo, pero como no existe una forma de interacción entre los objetos nativa e incompatible, supongo que esto es lo más parecido a comparar dos entidades en una matriz. Además, extender el objeto Array puede que no sea una buena idea, pero a veces está bien (si está al tanto de esto y de la compensación).



Yo uso lo siguiente:

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

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

a.contains('foo'); // true
a.contains('fox'); // false

b es el valor, y a es la matriz. Devuelve true o false :

function(a, b) {
    return a.indexOf(b) != -1
}

Actualización: como @orip menciona en los comentarios, el punto de referencia vinculado se realizó en 2008, por lo que los resultados pueden no ser relevantes para los navegadores modernos. Sin embargo, es probable que necesites esto para admitir navegadores no modernos de todos modos y probablemente no se hayan actualizado desde entonces. Siempre prueba por ti mismo.

Como han dicho otros, la iteración a través de la matriz es probablemente la mejor manera, pero se ha demostrado que un bucle de disminución es la forma más rápida de iterar en JavaScript. Así que es posible que desee volver a escribir su código de la siguiente manera:

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

Por supuesto, también puede extender el prototipo Array:

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

Y ahora puedes simplemente usar lo siguiente:

alert([1, 2, 3].contains(2)); // => true
alert([1, 2, 3].contains('2')); // => false

De ninguna manera lo mejor, pero me estaba volviendo creativo y añadiendo al repertorio.

No uses esto

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


function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

Array.prototype.some() se agregó al estándar ECMA-262 en la quinta edición





browser