Как сократить мои условные утверждения
У меня очень длинный условный оператор, например:
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
(Верхний набор: 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)) {
…
}
...