Verifique la igualdad de variables con una lista de valores

133

Estoy comprobando una variable, por ejemplo foo, para la igualdad de varios valores. Por ejemplo,

if( foo == 1 || foo == 3 || foo == 12 ) {
    // ...
}

El punto es que es bastante código para una tarea tan trivial. Se me ocurrió lo siguiente:

if( foo in {1: 1, 3: 1, 12: 1} ) {
    // ...
}

pero tampoco me atrae por completo, porque tengo que dar valores redundantes a los elementos del objeto.

¿Alguien sabe una forma decente de hacer una verificación de igualdad contra múltiples valores?

pimvdb
fuente
Creo que el contexto más amplio debe estar involucrado al decidir algo como esto, porque es importante saber por qué estás haciendo tales comparaciones.
Puntiagudo
Bueno, en un juego que estoy creando, compruebo los códigos del teclado para decidir qué función se debe llamar. En diferentes navegadores, una clave tiene diferentes códigos de tecla aparentemente, de ahí la necesidad de comparar con múltiples valores.
pimvdb
compruebe la prueba de rendimiento para varios métodos, el operador lógico gana runkit.com/pramendra/58cad911146c1c00147f8d8d
Pramendra Gupta

Respuestas:

175

Podría usar una matriz y indexOf:

if ([1,3,12].indexOf(foo) > -1)
Gumbo
fuente
1
Me gusta este. Incluso sería posible crear una función 'contiene' a través del prototipo, supongo, para eliminar el uso de> -1.
pimvdb
1
@pimvdb: Tenga en cuenta que es posible que necesite su propia implementación, ya indexOfque solo está disponible desde la 5ta edición de ECMAScript.
Gumbo
En otra respuesta esto se menciona como bien de hecho, no sabía que, gracias
pimvdb
¿Es '>' de alguna manera mejor que '! =='?
Max Waterman
117

En ECMA2016 puede utilizar la incluye método. Es la forma más limpia que he visto. (Compatible con todos los principales navegadores, excepto IE (Polyfill está en el enlace)

if([1,3,12].includes(foo)) {
    // ...
}
Alister
fuente
44
Para las variables if([a,b,c].includes(foo))o cadenasif(['a','b','c'].includes(foo))
hastig Zusammenstellen
para variables => if([a,b,c].includes(foo))porque las comillas lo convertirán en cadena. @HastigZusammenstellen
iambinodstha
2
@BinodShrestha Creo que eso es lo que escribí, a menos que me falte algo. "Para variables ... o cadenas ..."
Hastig Zusammenstellen
16

Usando las respuestas proporcionadas, terminé con lo siguiente:

Object.prototype.in = function() {
    for(var i=0; i<arguments.length; i++)
       if(arguments[i] == this) return true;
    return false;
}

Se puede llamar así:

if(foo.in(1, 3, 12)) {
    // ...
}

Editar: Me encontré con este 'truco' últimamente que es útil si los valores son cadenas y no contienen caracteres especiales. Para los caracteres especiales se vuelve feo debido al escape y también es más propenso a errores debido a eso.

/foo|bar|something/.test(str);

Para ser más precisos, esto verificará la cadena exacta, pero nuevamente es más complicado para una simple prueba de igualdad:

/^(foo|bar|something)$/.test(str);
pimvdb
fuente
55
¡Bondad! Me sorprende que funcione, ines una palabra clave en Javascript. Por ejemplo, ejecutar function in() {}; in()resultados en un error de sintaxis, al menos en Firefox, y no estoy seguro de por qué su código no lo hace. :-)
Ben Blank
3
Además, a menudo se considera una mala práctica extender Object.prototype, ya que afecta for (foo in bar)de manera desafortunada. ¿Quizás cambiarlo function contains(obj) { for (var i = 1; i < arguments.length; i++) if (arguments[i] === obj) return true; return false; }y pasar el objeto como argumento?
Ben Blank
Gracias por su reacción, y el Compilador de cierre de Google también devuelve un error al compilar mientras este código funciona de manera excelente. De todos modos, se ha sugerido que una clase auxiliar puede hacer este trabajo mejor que extender Object.prototypecomo también mencionas. Sin embargo, prefiero de esta manera, ya que la notación para la comprobación es encantadora foo.in(1, 2, "test", Infinity), sencilla y sencilla.
pimvdb
Y definir una función de in()la manera normal también falla en Chrome, pero el prototipo funciona ... :)
pimvdb
3
Extender el Object.prototype es un antipatrón y no debe usarse. Use una función en su lugar.
Vernon
9

Puede escribir if(foo in L(10,20,30))si define Lser

var L = function()
{
    var obj = {};
    for(var i=0; i<arguments.length; i++)
        obj[arguments[i]] = null;

    return obj;
};
Elian Ebbing
fuente
Eso es incluso más claro que las respuestas anteriores, ¡gracias!
pimvdb
2
Creo que es prudente señalar que las funciones prototipo también están 'en' una matriz / objeto, por lo que si hay una función 'eliminar' en un prototipo de matriz / objeto, siempre devolverá verdadero si codifica 'remove' in L(1, 3, 12), aunque no lo hizo especifique 'eliminar' para colocar en la lista.
pimvdb
7

Esto es fácilmente extensible y legible:

switch (foo) {
    case 1:
    case 3:
    case 12:
        // ...
        break

    case 4:
    case 5:
    case 6:
        // something else
        break
}

Pero no necesariamente más fácil :)

orlp
fuente
De hecho, utilicé este método porque quería poner un comentario al lado de cada valor para describir con qué se relacionaba el valor, esta era la forma más fácil de lograrlo.
pholcroft
@pholcroft si necesita describir con qué se relaciona cada valor, debe crear una enumeración o clase, y escribir sus propiedades para tener nombres descriptivos.
red_dorian
6
var a = [1,2,3];

if ( a.indexOf( 1 ) !== -1 ) { }

Tenga en cuenta que indexOf no está en el núcleo ECMAScript. Deberá tener un fragmento para IE y posiblemente otros navegadores que no admitan Array.prototype.indexOf.

if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(searchElement /*, fromIndex */)
  {
    "use strict";

    if (this === void 0 || 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 > 0)
    {
      n = Number(arguments[1]);
      if (n !== n)
        n = 0;
      else if (n !== 0 && n !== (1 / 0) && n !== -(1 / 0))
        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;
  };
}
meder omuraliev
fuente
Es la copia de Mozilla directamente. Era demasiado vago para vincular. developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
meder omuraliev
¿Hay alguna razón para el cambio de bits cuando define len si en realidad no está cambiando bits? ¿Hay alguna coerción implícita aquí?
jaredad7
3

Además, dado que los valores con los que está comprobando el resultado son únicos, también puede usar Set.prototype.has () .

var valid = [1, 3, 12];
var goodFoo = 3;
var badFoo = 55;

// Test
console.log( new Set(valid).has(goodFoo) );
console.log( new Set(valid).has(badFoo) );

Ivan Sivak
fuente
2

Ahora puede tener una mejor solución para resolver este escenario, pero de otra manera que prefiero.

const arr = [1,3,12]
if( arr.includes(foo)) { // it will return true if you `foo` is one of array values else false
  // code here    
}

Preferí la solución anterior sobre la verificación indexOf donde también necesita verificar el índice.

incluye documentos

if ( arr.indexOf( foo ) !== -1 ) { }
Javed Shaikh
fuente
1

Me gustó la respuesta aceptada, pero pensé que sería bueno permitirle tomar matrices también, así que la expandí a esto:

Object.prototype.isin = function() {
    for(var i = arguments.length; i--;) {
        var a = arguments[i];
        if(a.constructor === Array) {
            for(var j = a.length; j--;)
                if(a[j] == this) return true;
        }
        else if(a == this) return true;
    }
    return false;
}

var lucky = 7,
    more = [7, 11, 42];
lucky.isin(2, 3, 5, 8, more) //true

Puede eliminar la coerción de tipo cambiando ==a ===.

Greg Perham
fuente
1

Si tiene acceso a Underscore, puede usar lo siguiente:

if (_.contains([1, 3, 12], foo)) {
  // ...
}

contains solía trabajar en Lodash también (antes de V4), ahora tienes que usar includes

if (_.includes([1, 3, 12], foo)) {
  handleYellowFruit();
}
David Salamon
fuente
1

Esta es una pequeña función de ayuda auxiliar:

const letters = ['A', 'B', 'C', 'D'];

function checkInList(arr, val) {
  return arr.some(arrVal => val === arrVal);
}

checkInList(letters, 'E');   // false
checkInList(letters, 'A');   // true

Más información aquí ...

Víctor
fuente
Muy buen enfoque: a partir de su idea, también puede hacerlo sin una función auxiliar:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].some(option => option === valToCheck); // false
Giorgio Tempesta
Pero una vez que comencé a ajustar el código, descubrí que se includesconvierte en la mejor opción, como en la respuesta de @ alister:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].includes(valToCheck); // false
Giorgio Tempesta
0

Simplemente utilicé la función jQuery inArray y una matriz de valores para lograr esto:

myArr = ["A", "B", "C", "C-"];

var test = $.inArray("C", myArr)  
// returns index of 2 returns -1 if not in array

if(test >= 0) // true
ScottyG
fuente