Verifique a igualdade das variáveis ​​em uma lista de valores

133

Estou verificando uma variável, digamos foo, para igualdade com vários valores. Por exemplo,

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

O ponto é que é bastante código para uma tarefa tão trivial. Eu vim com o seguinte:

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

mas também isso não me atrai completamente, porque tenho que dar valores redundantes aos itens no objeto.

Alguém conhece uma maneira decente de fazer uma verificação de igualdade em relação a vários valores?

pimvdb
fonte
Acho que o contexto maior precisa estar envolvido ao decidir algo assim, porque é importante saber por que você está fazendo essas comparações.
Pointy
Bem, em um jogo que estou criando, estou verificando os códigos do teclado para decidir qual função deve ser chamada. Em navegadores diferentes, uma chave possui códigos de chave diferentes aparentemente, daí a necessidade de comparar com vários valores.
Pimvdb
verifique teste de desempenho de vários métodos, vitórias operador lógico runkit.com/pramendra/58cad911146c1c00147f8d8d
Pramendra Gupta

Respostas:

175

Você pode usar uma matriz e indexOf:

if ([1,3,12].indexOf(foo) > -1)
quiabo
fonte
1
Eu gosto deste. Seria até possível criar uma função 'contains' através do protótipo, eu acho, para eliminar o uso de> -1.
Pimvdb
1
@pimvdb: Observe que você pode precisar de sua própria implementação, pois indexOfsó está disponível desde a 5ª edição do ECMAScript.
Gumbo
Em outra resposta isso é mencionado, bem, na verdade, eu não sabia que, graças
pimvdb
'>' É de alguma forma melhor que '! =='?
Max Waterman
117

No ECMA2016, você pode usar o método includes . É a maneira mais limpa que eu já vi. (Compatível com todos os principais navegadores, exceto o IE (o Polyfill está no link)

if([1,3,12].includes(foo)) {
    // ...
}
Alister
fonte
4
Para variáveis if([a,b,c].includes(foo))ou cordasif(['a','b','c'].includes(foo))
Hastig Zusammenstellen
para variáveis ​​=> if([a,b,c].includes(foo))porque aspas fará com que seja uma string. @HastigZusammenstellen
iambinodstha
2
@BinodShrestha Acho que foi o que escrevi, a menos que esteja perdendo alguma coisa. "Para variáveis ​​.. ou strings ..."
Hastig Zusammenstellen 28/02/19
16

Usando as respostas fornecidas, acabei com o seguinte:

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

Pode ser chamado como:

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

Edit: me deparei com esse 'truque' ultimamente, que é útil se os valores são cadeias de caracteres e não contêm caracteres especiais. Para caracteres especiais, torna-se feio devido à fuga e também é mais suscetível a erros.

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

Para ser mais preciso, isso verificará a sequência exata, mas, novamente, é mais complicado para um simples teste de igualdade:

/^(foo|bar|something)$/.test(str);
pimvdb
fonte
5
Bondade! Estou surpreso que funcione - iné uma palavra-chave em Javascript. Por exemplo, a execução function in() {}; in()resulta em um erro de sintaxe, pelo menos no Firefox, e não sei por que seu código não. :-)
Ben Blank
3
Além disso, muitas vezes é considerado uma prática ruim estender-se Object.prototype, pois afeta de for (foo in bar)maneira infeliz. Talvez mude para function contains(obj) { for (var i = 1; i < arguments.length; i++) if (arguments[i] === obj) return true; return false; }e passe o objeto como argumento?
Ben Blank
Obrigado pela sua reação, e o Google Closure Compiler também retorna um erro durante a compilação, enquanto esse código funciona muito bem. De qualquer forma, foi sugerido que uma classe auxiliar possa fazer esse trabalho melhor do que estender, Object.prototypecomo você também mencionou. No entanto, prefiro assim, pois a notação para verificação é adorável foo.in(1, 2, "test", Infinity), fácil e fácil como torta.
Pimvdb
E definindo uma função in()da maneira regular falha no Chrome também, mas obras de protótipo ... :)
pimvdb
3
Estender o Object.prototype é um antipadrão e não deve ser usado. Use uma função em seu lugar.
Vernon
9

Você pode escrever if(foo in L(10,20,30))se definir Lcomo

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

    return obj;
};
Elian Ebbing
fonte
Isso é ainda mais claro do que as respostas anteriores, na verdade, obrigado!
Pimvdb
2
Eu acho que é prudente observar que as funções de protótipo também estão 'em' uma matriz / objeto - por isso, se houver uma função 'remover' no protótipo de uma matriz / objeto, ela sempre retornará true se você codificar 'remove' in L(1, 3, 12), embora não tenha especifique 'remover' para ser colocado na lista.
Pimvdb
7

Isso é facilmente extensível e legível:

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

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

Mas não necessariamente mais fácil :)

orlp
fonte
Na verdade, eu usei esse método, pois queria colocar um comentário ao lado de cada valor para descrever a que valor estava relacionado. Essa era a maneira mais legível de alcançá-lo.
pholcroft
@pholcroft, se você precisar descrever a que cada valor se relaciona, crie uma enumeração ou classe e escreva suas propriedades para ter nomes descritivos.
red_dorian
6
var a = [1,2,3];

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

Observe que indexOf não está no ECMAScript principal. Você precisará ter um snippet para o IE e possivelmente outros navegadores que não suportam 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
fonte
É a cópia da Mozilla diretamente. Estava com preguiça de ligar. developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
meder omuraliev
Existe uma razão para o deslocamento de bits quando você define len se realmente não está deslocando bits? Existe alguma coerção implícita acontecendo aqui?
precisa saber é o seguinte
3

Além disso, como os valores nos quais você está verificando o resultado são todos exclusivos, você também pode usar o 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
fonte
2

Agora você pode ter uma solução melhor para resolver esse cenário, mas de outra maneira que preferi.

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    
}

Eu preferi a solução acima da verificação indexOf, onde você também precisa verificar o índice.

inclui documentos

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

Gostei da resposta aceita, mas achei que seria legal permitir que ela também recebesse matrizes, então a expandi para isso:

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

Você pode remover a coerção de tipos alterando ==para ===.

Greg Perham
fonte
1

Se você tiver acesso ao Underscore, poderá usar o seguinte:

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

contains costumava trabalhar no Lodash também (antes da V4), agora você precisa usar includes

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

Esta é uma pequena função auxiliar arror:

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

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

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

Mais informações aqui ...

Vencedor
fonte
Abordagem muito boa: a partir da sua idéia, você também pode fazê-lo sem uma função auxiliar:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].some(option => option === valToCheck); // false
Giorgio Tempesta
Mas uma vez que comecei a ajustar o código, descobri que essa includesé a melhor opção, como na resposta de @ alister: #const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].includes(valToCheck); // false
Giorgio Tempesta
0

Eu simplesmente usei a função jQuery inArray e uma matriz de valores para fazer isso:

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
fonte