Переключение оператора для больше / меньше чем

230

поэтому я хочу использовать оператор switch следующим образом:

switch (scrollLeft) {
  case (<1000):
   //do stuff
   break;
  case (>1000 && <2000):
   //do stuff
   break;
}

Теперь я знаю, что любое из этих утверждений ( <1000) или ( >1000 && <2000) не будет работать (очевидно, по разным причинам). То, что я спрашиваю, является наиболее эффективным способом сделать это. Я ненавижу использовать 30 ifоператоров, поэтому я бы предпочел использовать синтаксис переключателя. Есть ли что-нибудь, что я могу сделать?

Switz
источник
5
Ваши шаги регулярны? Я имею в виду, если вы поделите scrollLeft на 1000, вы можете переключиться на 1, 2, 3 ...
IcanDivideBy0
Возможно, вы могли бы создать отсортированный массив, который отображает диапазон условий с соответствующей операцией, и применить к нему бинарный поиск. Или, если ваши условия достаточно регулярны, вы можете позвонить напрямую your_mapper_object[scrollLeft / SOME_CONST], предполагая, что- your_mapper_objectто вроде {1: some_func, 2: another_func, ...}. И в этом случае вы также можете использовать переключатель.
Сверхразум Цзяна

Ответы:

732

Когда я посмотрел на решения в других ответах, я увидел некоторые вещи, которые, как я знаю, плохо влияют на производительность. Я собирался поместить их в комментарий, но я подумал, что было бы лучше сравнить его и поделиться результатами. Вы можете проверить это сами . Ниже приведены мои результаты (ymmv), нормализованные после самой быстрой операции в каждом браузере (умножьте 1,0 раз на нормализованное значение, чтобы получить абсолютное время в мс).

                    Chrome Firefox Opera MSIE Safari Node
-------------------------------------------------- -----------------
1,0 время 37мс 73мс 68мс 184мс 73мс 21мс
если немедленный 1,0 1,0 1,0 2,6 1,0 1,0
если косвенный 1,2 1,8 3,3 3,8 2,6 1,0
switch-немедленный 2.0 1.1 2.0 1.0 2.8 1.3
диапазон переключения 38,1 10,6 2,6 7,3 20,9 10,4
switch-range2 31,9 8,3 2,0 4,5 9,5 6,9
переключатель-косвенный-массив 35,2 9,6 4,2 5,5 10,7 8,6
массив-линейный переключатель 3,6 4,1 4,5 10,0 4,7 2,7
массив-бинарный переключатель 7,8 6,7 9,5 16,0 15,0 4,9

Тест проводился в Windows 7 32bit с следующими версиями: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 . Узел был запущен на 64-битной системе Linux, поскольку разрешение таймера в Node.js для Windows составляло 10 мс, а не 1 мс.

если немедленное

Это самая быстрая во всех протестированных средах, за исключением ... барабанной дроби MSIE! (Сюрприз Сюрприз). Это рекомендуемый способ его реализации.

if (val < 1000) { /*do something */ } else
if (val < 2000) { /*do something */ } else
...
if (val < 30000) { /*do something */ } else

если косвенный

Это вариант, switch-indirect-arrayно с ifвместо -statements и работает намного быстрее, чем switch-indirect-arrayпочти во всех протестированных средах.

values=[
   1000,  2000, ... 30000
];
if (val < values[0]) { /* do something */ } else
if (val < values[1]) { /* do something */ } else
...
if (val < values[29]) { /* do something */ } else

включение немедленное

Это довольно быстро во всех протестированных средах и на самом деле самый быстрый в MSIE. Это работает, когда вы можете сделать расчет, чтобы получить индекс.

switch (Math.floor(val/1000)) {
  case 0: /* do something */ break;
  case 1: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

автоматическое включение фар

Это примерно в 6-40 раз медленнее, чем самая быстрая во всех протестированных средах, за исключением Opera, где она занимает примерно полтора раза. Это медленно, потому что двигатель должен сравнить значение дважды для каждого случая. Удивительно, но для выполнения этого Chrome требуется почти 40 раз больше времени по сравнению с самой быстрой операцией в Chrome, а MSIE - всего в 6 раз дольше. Но фактическая разница во времени составляла только 74 мс в пользу MSIE при 1337 мс (!).

switch (true) {
  case (0 <= val &&  val < 1000): /* do something */ break;
  case (1000 <= val &&  val < 2000): /* do something */ break;
  ...
  case (29000 <= val &&  val < 30000): /* do something */ break;
}

включение range2

Это вариант, switch-rangeно с одним сравнением в каждом случае, поэтому он быстрее, но все еще очень медленный, за исключением Opera. Порядок оператора case важен, так как механизм будет проверять каждый случай в порядке исходного кода. ECMAScript262: 5 12.11

switch (true) {
  case (val < 1000): /* do something */ break;
  case (val < 2000): /* do something */ break;
  ...
  case (val < 30000): /* do something */ break;
}

включение косвенного массива

В этом варианте диапазоны хранятся в массиве. Это медленно во всех протестированных средах и очень медленно в Chrome.

values=[1000,  2000 ... 29000, 30000];

switch(true) {
  case (val < values[0]): /* do something */ break;
  case (val < values[1]): /* do something */ break;
  ...
  case (val < values[29]): /* do something */ break;
}

массив линейного поиска

Это комбинация линейного поиска значений в массиве и оператора switch с фиксированными значениями. Причина, по которой можно использовать это, заключается в том, что значения неизвестны до времени выполнения. Он медленный в каждой тестируемой среде и занимает почти в 10 раз больше времени в MSIE.

values=[1000,  2000 ... 29000, 30000];

for (sidx=0, slen=values.length; sidx < slen; ++sidx) {
  if (val < values[sidx]) break;
}

switch (sidx) {
  case 0: /* do something */ break;
  case 1: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

Массив-двоично-переключатель

Это вариант, array-linear-switchно с бинарным поиском. К сожалению, это медленнее, чем линейный поиск. Я не знаю, является ли это моей реализацией или линейный поиск более оптимизирован. Возможно также, что пространство клавиш слишком маленькое.

values=[0, 1000,  2000 ... 29000, 30000];

while(range) {
  range = Math.floor( (smax - smin) / 2 );
  sidx = smin + range;
  if ( val < values[sidx] ) { smax = sidx; } else { smin = sidx; }
}

switch (sidx) {
  case 0: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

Вывод

Если важна производительность, используйте if-statements или switchс непосредственными значениями.

некоторые
источник
128
Редко можно увидеть ответ с такой подробной и аккуратной структурой. Большой +1
Рик Донохо
10
Большой +1 за объяснение стороны производительности этой проблемы!
Золтан Шмидт
16
Именно поэтому stackoverflow является одним из лучших мест для ответов. Это вечный ответ, отличная работа и спасибо за jsfiddle!
Джесси
1
Grt информация и объяснение
JayKandari
3
Я действительно хотел бы +2, такой подробный ответ!
Каспар Ли
96

Альтернатива:

var scrollleft = 1000;
switch (true)
{
    case (scrollleft > 1000):
      alert('gt');
      break;
    case (scrollleft <= 1000):
      alert('lt');
      break; 
}

Демо: http://jsfiddle.net/UWYzr/

labue
источник
4
это более ценное решение. +1
IcanDivideBy0
1
Разве это не так же, как if(...) else if(...)? Это избегает, ifно не совсем похоже на замену мне.
pimvdb
7
Несмотря на элегантность кода, это снижает производительность. Это почти в 30 раз медленнее в Chrome, чем использование if-statements. Смотрите мой ответ здесь
около
1
Однако такое снижение производительности незначительно, когда обрабатываемые данные невелики и, возможно, их применяют только к функции, например, для проверки ввода одного пользователя, тогда выбирается удобочитаемость, а не производительность в таком случае.
Хесус Франко
1
Это именно то, что я искал. Спасибо!
Ами Шрайбер
23
switch (Math.floor(scrollLeft/1000)) {
  case 0: // (<1000)
   //do stuff
   break;
  case 1: // (>=1000 && <2000)
   //do stuff;
   break;
}

Работает только если у вас есть регулярные шаги ...

РЕДАКТИРОВАТЬ: так как это решение продолжает получать голоса, я должен сказать, что решение Mofolo лучше

IcanDivideBy0
источник
1
Я использовал Math.round(scrollLeft/1000)кстати.
Switz
@Switz - просто имейте в виду, что 999 <1000 относится к случаю 0, а Math.round (999/1000) относится к случаю 1. Кроме того, выше приведена опечатка, в этом случае 1>> 1000, а не> 1000 ,
Игорь
Единственная проблема с решением mofolo состоит в том, что он примерно в 30 раз медленнее в Chrome, чем IcanDivideBy0. Смотри мой ответ ниже.
около
6

Вы можете создать пользовательский объект с критериями и функцией, соответствующей критериям

var rules = [{ lowerLimit: 0,    upperLimit: 1000, action: function1 }, 
             { lowerLimit: 1000, upperLimit: 2000, action: function2 }, 
             { lowerLimit: 2000, upperLimit: 3000, action: function3 }];

Определите функции для того, что вы хотите сделать в этих случаях (определите function1, function2 и т. Д.)

И "оценить" правила

function applyRules(scrollLeft)
{
   for(var i=0; i>rules.length; i++)
   {
       var oneRule = rules[i];
       if(scrollLeft > oneRule.lowerLimit && scrollLeft < oneRule.upperLimit)
       {
          oneRule.action();
       }
   }
}

Заметка

Я ненавижу использовать 30 если заявления

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

Обновление
Как @Brad указывал в комментариях, если условия являются взаимоисключающими (только одно из них может быть истинным одновременно), проверки верхнего предела должно быть достаточно:

if(scrollLeft < oneRule.upperLimit)

при условии, что условия определены в порядке возрастания (сначала самый низкий 0 to 1000, а затем, 1000 to 2000например,)

Нивы
источник
action=function1- разве это не должны быть двоеточия? ;-) - Вы также можете изменить его, чтобы иметь только верхний предел, так как из-за процесса исключения вы не можете попасть в две группы - если только это не было вашим намерением (иметь несколько возможных действий).
Брэд Кристи
@Brad Christie Конечно
Нивас
@Brad, нет, это не было моим намерением, и вы правы, верхний предел должен быть достаточным. Добавлю, что в качестве обновления ...
Nivas
Я нахожу это кратким и чистым +1
pimvdb
3

Что именно вы делаете в //do stuff?

Вы можете сделать что-то вроде:

(scrollLeft < 1000) ? //do stuff
: (scrollLeft > 1000 && scrollLeft < 2000) ? //do stuff
: (scrollLeft > 2000) ? //do stuff
: //etc. 
Игорь
источник
3

Непроверенный и неуверенный, будет ли это работать, но почему бы не сделать несколько if statementsранее, чтобы установить переменные для switch statement.

var small, big;

if(scrollLeft < 1000){
    //add some token to the page
    //call it small
}


switch (//reference token/) {
  case (small):
   //do stuff
   break;
  case (big):
   //do stuff;
   break;
}
Джейсон Дженнаро
источник
2

Это еще один вариант:

     switch (true) {
         case (value > 100):
             //do stuff
             break;
         case (value <= 100)&&(value > 75):
             //do stuff
             break;
         case (value < 50):
            //do stuff
             break;
     }
Пабло Клаус
источник
1

Обновление принятого ответа (пока не могу комментировать). Начиная с 1/12/16 с использованием демонстрационной версии jsfiddle в Chrome, Switch-немедленное является самым быстрым решением.

Результаты: Разрешение по времени: 1,33

   25ms "if-immediate" 150878146 
   29ms "if-indirect" 150878146
   24ms "switch-immediate" 150878146
   128ms "switch-range" 150878146
   45ms "switch-range2" 150878146
   47ms "switch-indirect-array" 150878146
   43ms "array-linear-switch" 150878146
   72ms "array-binary-switch" 150878146

Законченный

 1.04 (   25ms) if-immediate
 1.21 (   29ms) if-indirect
 1.00 (   24ms) switch-immediate
 5.33 (  128ms) switch-range
 1.88 (   45ms) switch-range2
 1.96 (   47ms) switch-indirect-array
 1.79 (   43ms) array-linear-switch
 3.00 (   72ms) array-binary-switch
jeffhale
источник
это действительно зависит - 15 мс, если немедленный, 15 мс, косвенный, 15 мс, немедленный переключатель, 37 мс, диапазон переключения, 28 мс, switch-range2, 35 мс, переключатель-непрямой массив, 29 мс, массив-линейный-переключатель. 62мс "массив-двоичный-переключатель" Закончено 1.00 (15мс) if-немедленный 1.00 (15мс) if-косвенный 1.00 (15мс) switch-немедленный 2.47 (37мс) switch-range 1.87 (28ms) switch-range2 2.33 (35ms) switch- косвенный-массив 1.93 (29 мс) массив-линейный-переключатель 4.13 (62 мс) массив-двоичный-переключатель chrome Версия 48.0.2564.109 (64-разрядная версия) mac os x 10.11.3
RenaissanceProgrammer
ATM Safari 9.X в Mac OS x и Safari ios 9.3, «если немедленный» - явный победитель
RenaissanceProgrammer
1
Разница в 1 мс слишком мала, чтобы о ней заботиться. Это отличается больше, чем от каждого теста. Дело в том, что используйте стиль кодирования, который имеет смысл, и не пытайтесь микрооптимизировать.
примерно
1

В моем случае (цветовое кодирование в процентах, ничего не критично к производительности) я быстро написал следующее:

function findColor(progress) {
    const thresholds = [30, 60];
    const colors = ["#90B451", "#F9A92F", "#90B451"];

    return colors.find((col, index) => {
        return index >= thresholds.length || progress < thresholds[index];
    });
}
grebenyuksv
источник
1

Я ненавижу использовать 30 если заявления

У меня была такая же ситуация в последнее время, вот как я ее решил:

перед:

if(wind_speed >= 18) {
    scale = 5;
} else if(wind_speed >= 12) {
    scale = 4;
} else if(wind_speed >= 9) {
    scale = 3;
} else if(wind_speed >= 6) {
    scale = 2;
} else if(wind_speed >= 4) {
    scale = 1;
}

после:

var scales = [[4, 1], [6, 2], [9, 3], [12, 4], [18, 5]];
scales.forEach(function(el){if(wind_speed > el[0]) scale = el[1]});

А если вы установите «1, 2, 3, 4, 5», то это может быть еще проще:

var scales = [4, 6, 9, 12, 18];
scales.forEach(function(el){if(wind_speed >= el) scale++});
Мартин
источник