Проверить равенство переменных по списку значений

134

Я проверяю переменную, скажем foo, на равенство ряду значений. Например,

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

Дело в том, что это довольно много кода для такой тривиальной задачи. Я придумал следующее:

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

но также это меня не полностью привлекает, потому что я должен дать избыточные значения элементам в объекте.

Кто-нибудь знает достойный способ проверки равенства нескольких значений?

pimvdb
источник
Я думаю, что при решении чего-то подобного необходимо задействовать более широкий контекст, потому что важно знать, почему вы проводите такие сравнения.
Pointy
Ну, в создаваемой мной игре я проверяю коды клавиатуры, чтобы решить, какую функцию следует вызвать. По-видимому, в разных браузерах ключ имеет разные коды, поэтому необходимо сравнивать несколько значений.
pimvdb
проверьте тест производительности для нескольких методов, логический оператор побеждает runkit.com/pramendra/58cad911146c1c00147f8d8d
Pramendra Gupta

Ответы:

175

Вы можете использовать массив и indexOf:

if ([1,3,12].indexOf(foo) > -1)
гумбо
источник
1
Мне нравится этот. Я думаю, можно было бы даже создать функцию 'содержит' через прототип, чтобы исключить использование> -1.
pimvdb
1
@pimvdb: обратите внимание, что вам может потребоваться ваша собственная реализация, поскольку indexOfона доступна только с 5-й редакции ECMAScript.
Gumbo
В другом ответе это тоже упоминается, я не знал этого, спасибо
pimvdb
">" Лучше чем "! =="?
Макс Уотерман
117

В ECMA2016 вы можете использовать включает в себя метод. Это самый чистый способ, который я видел. (Поддерживается всеми основными браузерами, кроме IE (Polyfill находится по ссылке)

if([1,3,12].includes(foo)) {
    // ...
}
Алистер
источник
4
Для переменных if([a,b,c].includes(foo))или строкif(['a','b','c'].includes(foo))
Хастиг Зусамменстеллен 01
для переменных =>, if([a,b,c].includes(foo))потому что кавычки сделают это строкой. @HastigZusammenstellen
iambinodstha
2
@BinodShrestha Думаю, это то, что я написал, если я что-то не упускаю. «Для переменных .. или строк ...»
Хастиг Зусамменстеллен
16

Используя предоставленные ответы, я получил следующее:

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

Это можно назвать так:

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

Изменить: в последнее время я столкнулся с этим «трюком», который полезен, если значения являются строками и не содержат специальных символов. Для специальных символов это становится уродливым из-за экранирования, а также из-за этого более подвержено ошибкам.

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

Чтобы быть более точным, это будет проверять точную строку, но опять же, это более сложно для простого теста на равенство:

/^(foo|bar|something)$/.test(str);
pimvdb
источник
5
Совершенство! Я поражен тем, что работает - inэто ключевое слово в Javascript. Например, запуск function in() {}; in()приводит к синтаксической ошибке, по крайней мере, в Firefox, и я не уверен, почему ваш код этого не делает. :-)
Бен Бланк
3
Кроме того, часто считается плохой практикой расширяться Object.prototype, так как это отрицательно сказывается for (foo in bar). Возможно, измените его на function contains(obj) { for (var i = 1; i < arguments.length; i++) if (arguments[i] === obj) return true; return false; }и передайте объект в качестве аргумента?
Бен Бланк,
Спасибо за вашу реакцию. Компилятор Google Closure также возвращает ошибку при компиляции, хотя сам код работает нормально. В любом случае, было высказано предположение, что вспомогательный класс может выполнять эту работу лучше, чем расширение, Object.prototypeо котором вы также упомянули. Однако я предпочитаю этот способ, так как обозначения для проверки прекрасны, просто foo.in(1, 2, "test", Infinity), проще простого.
pimvdb 04
И определение функции in()обычным способом в Chrome тоже не работает, но прототип работает ... :)
pimvdb
3
Расширение Object.prototype - это анти-шаблон, и его не следует использовать. Вместо этого используйте функцию.
Вернон
9

Вы можете писать, if(foo in L(10,20,30))если решите Lбыть

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

    return obj;
};
Элиан Эббинг
источник
Это даже яснее, чем предыдущие ответы, спасибо!
pimvdb
2
Я думаю, что разумно отметить, что функции прототипа также находятся «в» массиве / объекте - поэтому, если есть функция «удалить» в прототипе массива / объекта, она всегда будет возвращать истину, если вы кодируете 'remove' in L(1, 3, 12), хотя вы этого не сделали. укажите «удалить», чтобы добавить его в список.
pimvdb
7

Это легко расширить и прочитать:

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

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

Но не обязательно проще :)

orlp
источник
Я действительно использовал этот метод, так как хотел поставить комментарий рядом с каждым значением, чтобы описать, к чему это значение относится, это был наиболее читаемый способ его достижения.
pholcroft
@pholcroft, если вам нужно описать, к чему относится каждое значение, вы должны создать перечисление или класс и написать свои свойства так, чтобы они имели описательные имена.
red_dorian
6
var a = [1,2,3];

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

Обратите внимание, что indexOf отсутствует в ядре ECMAScript. Вам понадобится фрагмент кода для IE и, возможно, других браузеров, которые не поддерживают 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;
  };
}
Медер Омуралиев
источник
Это напрямую копия Mozilla. Лень было ссылаться. developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
медер омуралиев
Есть ли причина для битового сдвига при определении len, если вы на самом деле не сдвигаете биты? Происходит ли здесь какое-то неявное принуждение?
jaredad7 04
3

Кроме того, поскольку значения, по которым вы проверяете результат, уникальны, вы также можете использовать 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) );

Иван Сивак
источник
3

Теперь у вас может быть лучшее решение для разрешения этого сценария, но другой способ, который я предпочел.

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    
}

Я предпочел вышеуказанное решение проверке indexOf, где вам также нужно проверить index.

включает документы

if ( arr.indexOf( foo ) !== -1 ) { }
Джавед Шейх
источник
1

Мне понравился принятый ответ, но я подумал, что было бы неплохо разрешить ему также принимать массивы, поэтому я расширил его до этого:

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

Вы можете удалить приведение типов, изменив ==на ===.

Грег Перхэм
источник
1

Если у вас есть доступ к Underscore, вы можете использовать следующее:

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

contains раньше также работал в Lodash (до V4), теперь вам нужно использовать includes

if (_.includes([1, 3, 12], foo)) {
  handleYellowFruit();
}
Дэвид Саламон
источник
1

Это небольшая вспомогательная функция 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

Подробнее здесь ...

Виктор
источник
Очень хороший подход: исходя из вашей идеи, вы также можете сделать это без вспомогательной функции:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].some(option => option === valToCheck); // false
Джорджио Темпеста
Но как только я начал настраивать код, я обнаружил, что это includesстановится лучшим вариантом, как в ответе @alister:const valToCheck = 'E'; const check = ['A', 'B', 'C', 'D'].includes(valToCheck); // false
Джорджио Темпеста
0

Я просто использовал JQuery inArray функции и массив значений для достижения этой цели :

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
источник
-4

Для потомков вы можете использовать регулярные выражения в качестве альтернативы. Также довольно хорошая поддержка браузера (см. Https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match#Browser_compatibility )

Попробуй это

if (foo.toString().match(/^(1|3|12)$/)) {
    document.write('Regex me IN<br>');
} else {
    document.write('Regex me OUT<br>');
}
martisj
источник