Как сократить мои условные утверждения

У меня очень длинный условный оператор, например:

if(test.type == 'itema' || test.type == 'itemb' || test.type == 'itemc' || test.type == 'itemd'){
    // do something.
}

Мне было интересно, могу ли я реорганизовать это выражение/утверждение в более сжатую форму.

Любая идея о том, как достичь этого?

Ответ 1

Поместите свои значения в массив и проверьте, находится ли ваш элемент в массиве:

if ([1, 2, 3, 4].includes(test.type)) {
    // Do something
}

Если поддерживаемый браузер не имеет метода Array#includes, вы можете использовать этот polyfill.


Краткое описание ярлыка ~ тильды:

Обновление: Так как теперь у нас есть метод includes, больше нет смысла использовать ~ hack. Просто держите это здесь для людей, которым интересно знать, как это работает и/или встретилось с ним в другом коде.

Вместо того, чтобы проверить, является ли результат indexOf >= 0, есть небольшой небольшой ярлык:

if ( ~[1, 2, 3, 4].indexOf(test.type) ) {
    // Do something
}

Вот скрипка: http://jsfiddle.net/HYJvK/

Как это работает? Если элемент найден в массиве, indexOf возвращает свой индекс. Если элемент не найден, он вернет -1. Не вдаваясь в подробности, ~ является побитовым оператором NOT, который вернет 0 только для -1.

Мне нравится использовать ~ ярлык, так как он более краткий, чем сравнение с возвращаемым значением. Хотелось бы, чтобы у JavaScript была функция in_array, которая возвращает Boolean напрямую (аналогично PHP), но это просто принятие желаемого за действительное ( Обновление:, которое оно теперь делает. Оно называется includes. См. Выше). Обратите внимание, что jQuery inArray при совместном использовании сигнатуры метода PHP фактически имитирует встроенную функцию indexOf (что полезно в разных случаях, если индекс - это то, что вы действительно после).

Важное примечание: Использование ярлыка тильды, похоже, обмануто противоречиями, поскольку некоторые из них явно считают, что код недостаточно ясен и его следует избегать любой ценой (см. комментарии к этому ответу), Если вы разделяете их настроения, вы должны придерживаться решения .indexOf(...) >= 0.


Немного более подробное объяснение:

Целые числа в JavaScript подписываются, что означает, что самый левый бит зарезервирован как бит знака; флаг, указывающий, является ли число положительным или отрицательным, а 1 отрицательным.

Вот несколько примеров положительных чисел в 32-битном двоичном формате:

1 :    00000000000000000000000000000001
2 :    00000000000000000000000000000010
3 :    00000000000000000000000000000011
15:    00000000000000000000000000001111

Теперь вот те самые числа, но отрицательные:

-1 :   11111111111111111111111111111111
-2 :   11111111111111111111111111111110
-3 :   11111111111111111111111111111101
-15:   11111111111111111111111111110001

Почему такие странные комбинации для отрицательных чисел? Просто. Отрицательное число - это просто обратное положительное число + 1; добавление отрицательного числа к положительному числу должно всегда давать 0.

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

Вот как мы добавим -1 в +1:

   00000000000000000000000000000001      +1
+  11111111111111111111111111111111      -1
-------------------------------------------
=  00000000000000000000000000000000       0

И вот как мы добавим -15 в +15:

   00000000000000000000000000001111      +15
+  11111111111111111111111111110001      -15
--------------------------------------------
=  00000000000000000000000000000000        0

Как мы получаем эти результаты? Регулярно добавляя то, как нас учили в школе: вы начинаете с правой колонки, и вы складываете все строки. Если сумма больше наибольшего одноразрядного числа (которое в десятичном значении 9, но в двоичном выражении равно 1), мы переносим остаток в следующий столбец.

Теперь, как вы заметите, при добавлении отрицательного числа к его положительному числу, самый правый столбец, который не все 0, всегда будет иметь два 1 s, которые при объединении приводят к 2. Двоичное представление двух из них 10, мы переносим 1 в следующий столбец и помещаем a 0 для результата в первом столбце. Все остальные столбцы слева имеют только одну строку с 1, поэтому 1, перенесенный из предыдущего столбца, снова будет содержать до 2, который затем переносится... Этот процесс повторяется до тех пор, пока мы перейдите в самый левый столбец, где переносить 1 некуда, поэтому он переполняется и теряется, и мы все равно 0.

Эта система называется 2 дополнением. Подробнее об этом можно узнать здесь:

2 Представление комплемента для подписанных целых чисел.


Теперь, когда завершился краш-курс в 2 дополнениях, вы заметите, что -1 - это единственное число, двоичное представление которого 1 все.

Используя оператор ~ побитового NOT, все биты в заданном числе инвертируются. Единственный способ вернуть 0 обратно из инвертирования всех битов - это если мы начали с 1 все попеременно.

Итак, все это был длинный способ сказать, что ~n вернет только 0, если n - -1.

Ответ 2

Вы можете использовать оператор switch с падением:

switch (test.type) {

  case "itema":
  case "itemb":
  case "itemc":
  case "itemd":
    // do something
}

Ответ 3

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

ЭТО БЫСТРО, ЧЕМ ~ Метод

var x = test.type;
if (x == 'itema' ||
    x == 'itemb' ||
    x == 'itemc' ||
    x == 'itemd') {
    //do something
}

http://jsperf.com/if-statements-test-techsin enter image description here (Верхний набор: Chrome, нижний набор: Firefox)

Вывод:

Если возможности несколько, и вы знаете, что некоторые из них более вероятны, чем вы получаете максимальную производительность if ||, switch fall through и if(obj[keyval]).

Если возможности много, и любой из них может быть наиболее встречным, другими словами, вы не можете знать, какой из них наиболее вероятен чем вы получаете большую производительность из поиска объектов if(obj[keyval]) и regex, если это соответствует.

http://jsperf.com/if-statements-test-techsin/12

Я обновлю, если появится что-то новое.

Ответ 4

Если вы сравниваете строки и есть шаблон, рассмотрите возможность использования регулярных выражений.

В противном случае, я подозреваю, что попытка сократить его будет просто запутывать ваш код. Рассмотрим просто обертывание строк, чтобы сделать его довольно.

if (test.type == 'itema' ||
    test.type == 'itemb' ||
    test.type == 'itemc' ||
    test.type == 'itemd') {
    do something.
}

Ответ 5

var possibilities = {
  "itema": 1,
  "itemb": 1,
  "itemc": 1,
…};
if (test.type in possibilities) { … }

Использование объекта как ассоциативного массива - довольно обычная вещь, но поскольку JavaScript не имеет собственного набора, вы также можете использовать объекты как дешевые.

Ответ 6

if( /^item[a-d]$/.test(test.type) ) { /* do something */ }

или если элементы не являются одинаковыми, то:

if( /^(itema|itemb|itemc|itemd)$/.test(test.type) ) { /* do something */ }

Ответ 7

Отличные ответы, но вы можете сделать код более читаемым, обернув один из них в функцию.

Это сложный оператор if, когда вы (или кто-то еще) читаете код через много лет, вы будете сканировать, чтобы найти раздел, чтобы понять, что происходит. Заявление с таким уровнем бизнес-логики заставит вас наткнуться на несколько секунд, пока вы будете работать над тем, что вы тестируете. Где в качестве такого кода вы сможете продолжить сканирование.

if(CheckIfBusinessRuleIsTrue())
{
    //Do Something
}

function CheckIfBusinessRuleIsTrue() 
{
    return (the best solution from previous posts here);
}

Назовите свою функцию явно, чтобы она сразу же поняла, что вы тестируете, и ваш код будет намного проще сканировать и понимать.

Ответ 8

Вы можете поместить все ответы в Javascript Set, а затем просто вызвать .contains() в комплекте.

Вам все равно нужно объявить все содержимое, но встроенный вызов будет короче.

Что-то вроде:

var itemSet = new Set(["itema","itemb","itemc","itemd"]);
if( itemSet.contains( test.type ){}

Ответ 9

Один из моих любимых способов выполнить это - это библиотека, такая как underscore.js...

var isItem = _.some(['itema','itemb','itemc','itemd'], function(item) {
    return test.type === item;
});

if(isItem) {
    // One of them was true
}

http://underscorejs.org/#some

Ответ 10

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

if ('a' in oc(['a','b','c'])) { //dosomething }

function oc(a)
{
  var o = {};
  for(var i=0;i<a.length;i++)  o[a[i]]='';
  return o;
}

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

http://snook.ca/archives/javascript/testing_for_a_v

с использованием операторов, таких как ~ && || ((),()) ~~ отлично, только если ваш код ломается позже. Вы не будете знать, с чего начать. Поэтому читаемость BIG.

если вы можете сделать это короче.

('a' in oc(['a','b','c'])) && statement;
('a' in oc(['a','b','c'])) && (statements,statements);
('a' in oc(['a','b','c']))?statement:elseStatement;
('a' in oc(['a','b','c']))?(statements,statements):(elseStatements,elseStatements);

и если вы хотите сделать обратный

('a' in oc(['a','b','c'])) || statement;

Ответ 11

Просто используйте инструкцию switch вместо инструкции if:

switch (test.type) {

  case "itema":case "itemb":case "itemc":case "itemd":
    // do your process
  case "other cases":...:
    // do other processes
  default:
    // do processes when test.type does not meet your predictions.
}

switch также работает быстрее, чем сравнение множества условных выражений в if

Ответ 12

Для очень длинных списков строк эта идея сохранит несколько символов (не говоря, что я рекомендую ее в реальной жизни, но она должна работать).

Выберите символ, который, как вы знаете, не будет отображаться в вашем тесте test.type, используйте его как разделитель, вставьте все в одну длинную строку и выполните поиск:

if ("/itema/itemb/itemc/itemd/".indexOf("/"+test.type+"/")>=0) {
  // doSomething
}

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

if ("itemaitembitemcitemd".indexOf(test.type)>=0) {
  // doSomething
}

... но в этом случае вы должны быть осторожны с ложными срабатываниями (например, "embate" будет соответствовать в этой версии)

Ответ 13

Я думаю, что есть две цели при написании такого рода условий if.

  • Краткость
  • читабельность

Как таковой иногда # 1 может быть самым быстрым, но я возьму # 2 для легкого обслуживания позже. В зависимости от сценария я часто выбираю вариант ответа Уолтера.

Чтобы начать, у меня есть глобально доступная функция как часть моей существующей библиотеки.

function isDefined(obj){
  return (typeof(obj) != 'undefined');
}

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

var validOptions = {
  "itema":1,
  "itemb":1,
  "itemc":1,
  "itemd":1
};
if(isDefined(validOptions[test.type])){
  //do something...
}

Это не так быстро, как оператор switch/case и немного более подробный, чем некоторые другие примеры, но я часто получаю повторное использование объекта в другом месте кода, который может быть весьма удобным.

На основе одной из приведенных выше проб jsperf я добавил этот тест и вариант для сравнения скоростей. http://jsperf.com/if-statements-test-techsin/6 Самое интересное, что я заметил, это то, что некоторые тестовые комбо в Firefox намного быстрее, чем даже Chrome.

Ответ 14

Это можно решить с помощью простого цикла:

test = {};
test.type = 'itema';

for(var i=['itema','itemb','itemc']; i[0]==test.type && [
    (function() {
        // do something
        console.log('matched!');
    })()
]; i.shift());

Мы используем первый раздел цикла for, чтобы инициализировать аргументы, которые вы хотите сопоставить, второй раздел, чтобы остановить цикл for, и третий раздел, чтобы вызвать завершение цикла.

Ответ 15

Для удобства чтения создайте функцию для теста (да, одна строка):

function isTypeDefined(test) {
    return test.type == 'itema' ||
           test.type == 'itemb' ||
           test.type == 'itemc' ||
           test.type == 'itemd';
}

затем назовите его:

…
    if (isTypeDefined(test)) {
…
}
...