Оператор переключення на більший за / менший за


230

тому я хочу використовувати оператор переключення, як це:

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

Тепер я знаю, що жоден із цих тверджень ( <1000) або ( >1000 && <2000) не буде працювати (очевидно, з різних причин). Що я прошу - це найефективніший спосіб зробити саме це. Я ненавиджу використання 30 ifоператорів, тому я краще використовувати синтаксис перемикача. Чи є щось, що я можу зробити?


5
чи ваші кроки регулярні? Я маю на увазі, якщо розділити прокруткуLeft на 1000, ви можете переключити 1, 2, 3 ...
IcanDivideBy0

Можливо, ви могли б зробити відсортований масив, який відображає діапазон стану відповідної операції, і застосувати до нього двійковий пошук. Або якщо ваші умови достатньо регулярні, ви можете безпосередньо зателефонувати your_mapper_object[scrollLeft / SOME_CONST], припустивши, що your_mapper_objectце щось подібне {1: some_func, 2: another_func, ...}. І в цьому випадку ви також можете використовувати перемикач.
Перемогти Цзян

Відповіді:


731

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

                    Сафарі-вузол Chrome Firefox Opera MSIE
-------------------------------------------------- -----------------
1,0 час 37ms 73ms 68ms 184ms 73ms 21ms
if-безпосередній 1,0 1,0 1,0 2,6 1,0 1,0
якщо-непрямі 1,2 1,8 3,3 3,8 2,6 1,0
перемикач-негайний 2.0 1.1 2.0 1.0 2.8 1.3
діапазон комутаторів 38,1 10,6 2,6 7,3 20,9 10,4
перемикач-діапазон2 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 . Node запускався на 64-бітній коробці Linux, оскільки роздільна здатність таймера для Node.js для Windows становила 10 мс замість 1 мс.

якщо-негайний

Це найшвидший у всіх перевірених середовищах, за винятком ... MSD ! (здивування, здивування). Це рекомендований спосіб його реалізації.

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

якщо-непрямий

Це варіант, switch-indirect-arrayале ifзамість-заяв замість цього працює набагато швидше, ніж 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;
}

комутатор-діапазон2

Це варіант, switch-rangeале лише з одним порівнянням на випадок, і тому швидше, але все ще дуже повільно, за винятком Opera. Порядок викладу справи важливий, оскільки двигун перевірятиме кожен випадок у порядку вихідного коду 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;
}

масив-лінійний-пошук

Це комбінація лінійного пошуку значень у масиві та оператора перемикання із фіксованими значеннями. Причина, з якою можна скористатися цим, полягає в тому, що значення не відомі до часу виконання. Він повільний у кожному тестованому середовищі і займає майже 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-binary-switch

Це варіант, 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твердження або 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/


4
це більш цінне рішення. +1
IcanDivideBy0

1
Хіба це не те саме, що if(...) else if(...)? Це не дозволяє, ifале це не дуже звучить, як гарна заміна для мене.
pimvdb

7
Хоча елегантно кодувати, це шкодить продуктивності. Це майже в 30 разів повільніше в Chrome, ніж використання if-state. Дивіться мою відповідь тут
якихось

1
Однак таке покарання за ефективність є незначним, коли оброблювані дані не є великими, і, можливо, застосовується лише його функція, наприклад перевірка одного вводу користувача, тоді читальність вибирається, а не продуктивність у такому випадку.
Jesús Franco

1
Це саме те, що я шукав. Дякую!
Амі Шрайбер

23
switch (Math.floor(scrollLeft/1000)) {
  case 0: // (<1000)
   //do stuff
   break;
  case 1: // (>=1000 && <2000)
   //do stuff;
   break;
}

Працює лише за регулярних кроків ...

EDIT: оскільки це рішення продовжує отримувати надбавки, я мушу порадити, що рішення mofolo є набагато кращим


1
Я використовував Math.round(scrollLeft/1000)до речі.
перемикач

@Switz - Пам’ятайте лише, що 999 <1000 потрапляє у випадок 0, але Math.round (999/1000) потрапляє у випадок 1. Також є помилка друку вище, у цьому випадку 1>> = 1000, а не просто> 1000 .
Ігор

Єдина проблема з рішенням mofolo полягає в тому, що в Chrome це приблизно в 30 разів повільніше, ніж у IcanDivideBy0. Ось моя відповідь нижче.
десь

6

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

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

Визначте функції, що ви хочете робити в цих випадках (визначте функцію1, функцію2 тощо)

І «оцініть» правила

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 Of Course
Nivas

@ Браде, ні, це не було моїм наміром, і ти маєш рацію, верхня межа повинна бути достатньою. Додамо це як оновлення ...
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 в хромі, перемикання негайне - це найшвидше рішення.

Результати: Часова роздільна здатність: 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

це дійсно залежить - 15ms "if-негайний" 15ms "if-indirect" 15ms "switch-neposredний" 37ms "діапазон комутаторів" 28ms "switch-range2" 35ms "switch-indirect-array" 29ms "array-linear-switch" 62мс "масив-бінарний перемикач" Готово 1.00 (15 мс), якщо негайне 1.00 (15 мс), якщо непряме 1.00 (15 мс) перемикач - негайне 2.47 (37 мс) діапазон комутаторів 1.87 (28 мс) перемикач-діапазон2 2.33 (35 мс) непрямий масив 1.93 (29 мс) array-linear-switch 4.13 (62ms) array-binary-switch chrome Версія 48.0.2564.109 (64-бітний) mac os x 10.11.3
RenaissanceProgrammer

Банкомат Safari 9.X на Mac OS x та Safari ios 9.3, "якщо-негайний" - явний переможець
Ренесанс"

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];
    });
}

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++});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.