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

Esta es la única forma en que sé 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 de lograr esto?

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

3932
brad 26 oct. 2008 a las 02:14

21 respuesta

Utilice la función alguna de lodash.

Es conciso, preciso y tiene un gran soporte multiplataforma.

La respuesta aceptada ni siquiera cumple los requisitos.

Requisitos: Recomiende la forma más concisa y eficiente de averiguar si una matriz de JavaScript contiene un objeto.

Respuesta aceptada:

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

Mi recomendación:

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

Notas:

$ .inArray funciona bien para determinar si existe un valor escalar en una matriz de escalares ...

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

... pero la pregunta claramente pide una manera eficiente de determinar si un objeto está contenido en una matriz.

Para manejar tanto escalares como objetos, puede hacer esto:

(_.isObject(item)) ? _.some(ary, item) : (_.indexOf(ary, item) > -1)
11
l3x 21 oct. 2015 a las 11:58

Uso:

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

// Usage
if(isInArray(my_array, "my_value"))
{
    //...
}
55
Peter Mortensen 7 ene. 2017 a las 11:29

ECMAScript 7 presenta Array.prototype.includes .

Se puede utilizar así:

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

También acepta un segundo argumento opcional fromIndex:

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

A diferencia de indexOf, que utiliza Comparación estricta de igualdad, includes se compara usando SameValueZero algoritmo de igualdad. Eso significa que puede detectar si una matriz incluye un NaN:

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

Además, a diferencia de indexOf, includes no omite los índices faltantes:

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

Actualmente todavía es un borrador, pero puede ser polyfilled para que funcione en todos los navegadores.

166
Oriol 8 feb. 2016 a las 16:53

indexOf quizás, pero es una "extensión de JavaScript" al estándar ECMA-262; como tal, puede no estar presente en otras implementaciones del estándar ".

Ejemplo:

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

AFAICS Microsoft no no ofrece algún tipo de alternativa a esto, pero puede agregar una funcionalidad similar a las matrices en Internet Explorer (y otros navegadores que no admiten indexOf) si lo desea, como la búsqueda rápida en Google revela (por ejemplo, este).

201
Peter Mortensen 11 ago. 2011 a las 23:41

Uso:

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

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

Demo

Para saber exactamente qué hacen los tilde ~ en este punto, consulte esta pregunta ¿Qué hace una tilde cuando precede a una expresión? .

8
Community 23 may. 2017 a las 12:03

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;
        };
}
77
Narendra Jadhav 30 ene. 2019 a las 10:23

Se puede usar 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'));
8
J D 24 ene. 2020 a las 12:16
function inArray(elem,array)
{
    var len = array.length;
    for(var i = 0 ; i < len;i++)
    {
        if(array[i] == elem){return i;}
    }
    return -1;
} 

Devuelve el índice de matriz si se encuentra, o -1 si no se encuentra

15
mpromonet 6 sep. 2014 a las 16:22
function contains(a, obj) {
    return a.some(function(element){return element == obj;})
}

Array.prototype.some () se agregó a la ECMA -262 estándar en la 5ta edición

23
dansalmo 12 sep. 2014 a las 16:55

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

14
Andru Luvisi 25 oct. 2008 a las 22:44

Usamos este fragmento (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
15
dr.dimitru 10 sep. 2014 a las 12:12

Si está comprobando repetidamente la existencia de un objeto en una matriz, tal vez debería investigar

  1. Mantener la matriz ordenada en todo momento haciendo clasificación de inserción en su matriz (coloque nuevos objetos en el lugar correcto)
  2. Realice la actualización de objetos como eliminar + operación de inserción ordenada y
  3. Utilice una búsqueda binaria en su contains(a, obj).
14
Ztyx 5 feb. 2011 a las 18:02

OK, ¡puedes optimizar tu 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 aplicarlo 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;
}
6
Alireza 6 feb. 2019 a las 14:48

Si bien array.indexOf(x)!=-1 es la forma más concisa de hacer esto (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 (N), que es terrible. Si su matriz no va a cambiar, puede convertirla en una tabla hash, luego haga table[x]!==undefined o ===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})

(Desafortunadamente, si bien 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 elige editar su matriz más adelante. JavaScript no tiene suficientes enlaces para le permite mantener este estado, a diferencia de Python, por ejemplo).

9
Peter Mortensen 7 ene. 2017 a las 11:30

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

Si no necesita preocuparse por la compatibilidad con otros JavaScript que se ejecutan 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.

48
Peter Mortensen 11 ago. 2011 a las 23:43

Una solución simple para este requisito es usar find()

Si tiene una variedad de objetos como a continuación,

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

Luego puede verificar si el objeto con su valor ya está presente o no

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

Si los datos son nulos, entonces no hay administrador; de lo contrario, devolverá el objeto existente como se muestra a continuación.

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

Luego puede encontrar el índice de ese objeto en la matriz y reemplazar el objeto usando el código a continuación.

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

Obtendrá valor como a continuación

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

Espero que esto ayude a cualquiera.

5
Shiva 11 oct. 2019 a las 17:38
    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);
5
J D 24 ene. 2020 a las 12:01

Actuación

Hoy 2020.01.07 realizo pruebas en MacOs HighSierra 10.13.6 en Chrome v78.0.0, Safari v13.0.4 y Firefox v71.0.0 para 15 soluciones elegidas. Conclusiones

  • Las soluciones basadas en JSON, Set y sorprendentemente find (K, N, O) son las más lentas en todos los navegadores
  • el es6 includes (F) es rápido solo en Chrome
  • las soluciones basadas en for (C, D) y indexOf (G, H) son bastante rápidas en todos los navegadores en arreglos pequeños y grandes, por lo que probablemente sean la mejor opción para una solución eficiente
  • las soluciones donde el índice disminuye durante el ciclo, (B) es más lento probablemente porque la forma de El caché de la CPU funciona.
  • También ejecuté la prueba de matriz grande cuando el elemento buscado estaba en la posición 66% de la longitud de la matriz, y las soluciones basadas en for (C, D, E) dan resultados similares (~ 630 operaciones / seg, pero la E en safari y Firefox fue 10-20% más lento que C y D)

Resultados

enter image description here

Detalles

Realizo 2 casos de prueba: para matriz con 10 elementos y matriz con 1 millón de elementos. En ambos casos, colocamos el elemento buscado en el centro de la matriz.

let log = (name,f) => console.log(`${name}: 3-${f(arr,'s10')}  's7'-${f(arr,'s7')}  6-${f(arr,6)} 's3'-${f(arr,'s3')}`)

let arr = [1,2,3,4,5,'s6','s7','s8','s9','s10'];
//arr = new Array(1000000).fill(123); arr[500000]=7;

function A(a, val) {
    var i = -1;
    var n = a.length;
    while (i++<n) {
       if (a[i] === val) {
           return true;
       }
    }
    return false;
}

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

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

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

function E(a, val){  
  var n = a.length-1;
  var t = n/2;
  for (var i = 0; i <= t; i++) {
        if (a[i] === val || a[n-i] === val) return true;
  }
  return false;
}

function F(a,val) {
	return a.includes(val);
}

function G(a,val) {
	return a.indexOf(val)>=0;
}

function H(a,val) {
	return !!~a.indexOf(val);
}

function I(a, val) {
  return a.findIndex(x=> x==val)>=0;
}

function J(a,val) {
	return a.some(x=> x===val);
}

function K(a, val) {
  const s = JSON.stringify(val);
  return a.some(x => JSON.stringify(x) === s);
}

function L(a,val) {
	return !a.every(x=> x!==val);
}

function M(a, val) {
  return !!a.find(x=> x==val);
}

function N(a,val) {
	return a.filter(x=>x===val).length > 0;
}

function O(a, val) {
  return new Set(a).has(val);
}

log('A',A);
log('B',B);
log('C',C);
log('D',D);
log('E',E);
log('F',F);
log('G',G);
log('H',H);
log('I',I);
log('J',J);
log('K',K);
log('L',L);
log('M',M);
log('N',N);
log('O',O);
This shippet only presents functions used in performance tests - it not perform tests itself!

Matriz pequeña: 10 elementos

Puede realizar pruebas en su máquina AQUÍ

enter image description here

Matriz grande: 1.000.000 de elementos

Puede realizar pruebas en su máquina AQUÍ

enter image description here

11
Kamil Kiełczewski 11 mar. 2020 a las 16:26

Un trazador de líneas:

function contains(arr, x) {
    return arr.filter(function(elem) { return elem == x }).length > 0;
}
31
Peter Mortensen 7 ene. 2017 a las 11:25

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
26
Eduardo Cuomo 15 jun. 2014 a las 01:15