Büyüktür / küçüktür için switch deyimi


230

bu yüzden böyle bir switch deyimi kullanmak istiyorum:

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

Şimdi bu ifadelerden herhangi birinin ( <1000) veya ( >1000 && <2000) işe yaramayacağını biliyorum (farklı nedenlerle, belli ki). Sorduğum şey, bunu yapmanın en etkili yoludur. 30 ifdeyim kullanmaktan nefret ediyorum , bu yüzden anahtar sözdizimini kullanmak istiyorum. Yapabileceğim bir şey var mı?


5
adımların düzenli mi? Yani, scrollLeft'i 1000'e bölerseniz, 1, 2, 3'ü değiştirebilirsiniz ...
IcanDivideBy0

Belki karşılık gelen işlemle bir koşul aralığını eşleyen sıralı bir dizi oluşturabilir ve üzerine bir ikili arama uygulayabilirsiniz. Ya da koşullarınız yeterince your_mapper_object[scrollLeft / SOME_CONST]düzenliyse your_mapper_object, doğrudan bir şey diyebilirsiniz , varsayalım {1: some_func, 2: another_func, ...}. Ve bu durumda anahtarı da kullanabilirsiniz.
Over Jind Jiang

Yanıtlar:


731

Diğer cevaplardaki çözümlere baktığımda performans için kötü olduğunu bildiğim bazı şeyler gördüm. Onları bir yoruma ekleyecektim ama karşılaştırmalı değerlendirmenin ve sonuçları paylaşmanın daha iyi olduğunu düşündüm. Kendiniz test edebilirsiniz . Aşağıda, her tarayıcıdaki en hızlı işlemden sonra normalleştirilmiş sonuçlarım (ymmv) (ms cinsinden mutlak zamanı elde etmek için 1.0 zamanı normalleştirilmiş değerle çarpın).

                    Chrome Firefox Opera MSIE Safari Düğümü
-------------------------------------------------- -----------------
1.0 zaman 37ms 73ms 68ms 184ms 73ms 21ms
if-instant 1.0 1.0 1.0 2.6 1.0 1.0
if-dolaylı 1.2 1.8 3.3 3.8 2.6 1.0
Anında Açılma 2.0 1.1 2.0 1.0 2.8 1.3
anahtarlama aralığı 38,1 10,6 2,6 7,3 20,9 10,4
anahtar aralığı2 31,9 8,3 2,0 4,5 9,5 6,9
anahtar-dolaylı dizi 35,2 9,6 4,2 5,5 10,7 8,6
dizi-doğrusal-anahtar 3.6 4.1 4.5 10.0 4.7 2.7
dizi-ikili-anahtar 7.8 6.7 9.5 16.0 15.0 4.9

Windows 7 32bit'te aşağıdaki sürümlerle gerçekleştirildiği yerleri test edin: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 . Windows için Node.js'de zamanlayıcı çözünürlüğü 1ms yerine 10ms olduğu için düğüm bir Linux 64bit kutusunda çalıştırıldı.

if-acil

Bu hariç tüm test ortamlarda en hızlı, ... drumroll MSIE! (sürpriz sürpriz). Uygulamanın önerilen yolu budur.

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

if-dolaylı

Bu bir çeşididir switch-indirect-arrayfakat ifdaha hızlı daha yerine -statements ve gerçekleştirir switch-indirect-arrayneredeyse tüm test ortamlarında.

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

anahtar-acil

Bu, test edilen tüm ortamlarda oldukça hızlıdır ve aslında MSIE'deki en hızlısıdır. Bir dizin almak için bir hesaplama yapabildiğinizde çalışır.

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

Açma aralığı

Bu, Opera'nın dışında yaklaşık bir buçuk kat daha uzun sürdüğü test edilen tüm ortamlardaki en hızlıdan 6 ila 40 kat daha yavaştır. Yavaştır, çünkü motor her durum için değeri iki kez karşılaştırmak zorundadır. Şaşırtıcı bir şekilde Chrome'un Chrome'daki en hızlı işleme kıyasla bunu tamamlaması yaklaşık 40 kat daha uzun sürerken, MSIE yalnızca 6 kat daha uzun sürüyor. Ancak gerçek zaman farkı 1337 ms'de MSIE lehine sadece 74 ms idi (!).

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

anahtar-aralık2

Bu bir varyanttır, switch-rangeancak vaka başına sadece bir karşılaştırma ve bu nedenle Opera hariç daha hızlı, ancak yine de çok yavaştır. Vaka deyiminin sırası önemlidir, çünkü motor her vakayı kaynak kod sırası ECMAScript262'de test edecektir : 5 12.11

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

Anahtar-dolaylı dizi

Bu varyantta aralıklar bir dizide saklanır. Bu, test edilen tüm ortamlarda yavaş ve Chrome'da çok yavaş.

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

dizi doğrusal arama

Bu, bir dizideki değerlerin doğrusal aramasının ve switch ifadesinin sabit değerlerle birleşimidir. Birinin bunu kullanmak isteyebilmesinin nedeni, çalışma zamanına kadar değerlerin bilinmemesidir. Test edilen her ortamda yavaştır ve MSIE'de yaklaşık 10 kat daha uzun sürer.

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

Dizi-ikili-anahtar

Bu, array-linear-switchikili arama içeren bir varyanttır . Ne yazık ki doğrusal aramadan daha yavaştır. Uygulamam mı yoksa doğrusal arama daha mı optimize edilmiş mi bilmiyorum. Anahtar boşluğunun küçük olması da olabilir.

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

Sonuç

Performans önemliyse, if-statements veya switchanlık değerlerle kullanın.


128
Bu kadar ayrıntılı ve düzenli yapı ile bir cevap görmek nadirdir. Büyük +1
Rick Donohoe

10
Bu sorunun performans tarafının açıklaması için büyük +1!
Zoltán Schmidt

16
Stackoverflow'un cevaplar için en iyi yerlerden biri olmasının nedeni budur. Bu "zamansız" bir cevap, harika bir iş ve jsfiddle için teşekkürler!
Jessy

1
grt bilgi ve açıklama
JayKandari

3
Keşke +2 yapabilsem, bu kadar ayrıntılı bir cevap!
Kaspar Lee

96

Bir alternatif:

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

Demo: http://jsfiddle.net/UWYzr/


4
bu daha değerli bir çözümdür. +1
IcanDivideBy0

1
Bu aynı şey değil if(...) else if(...)mi? Bu kaçınıyor ifama benim yerine oldukça hoş görünmüyor.
pimvdb

7
Kodlamak zarif olsa da, performansı acıtıyor. Chrome'da if-statements kullanmaktan neredeyse 30 kat daha yavaştır . Benim Bkz burada cevap
Bazı

1
Ancak, işlenen veriler büyük olmadığında ve belki de sadece tek bir kullanıcı girişini onaylamak gibi bir işlev uygulandığında bu tür performans cezası ihmal edilebilir, bu durumda performans yerine okunabilirlik seçilir.
Jesús Franco

1
Tam da aradığım şey buydu. Teşekkürler!
Ami Schreiber

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

Yalnızca düzenli adımlarınız varsa çalışır ...

DÜZENLEME: Bu çözüm oy almaya devam ettiğinden, mofolo'nun çözümünün çok daha iyi olduğunu tavsiye etmeliyim


1
Bu arada kullandım Math.round(scrollLeft/1000).
switz

@Switz - 999 <1000'in 0'a düştüğünü, ancak Math.round'un (999/1000) 1'e düştüğünü unutmayın. Ayrıca, yukarıda bir yazım hatası vardır, bu durumda 1> = 1000'dir, sadece> 1000 değil .
Igor

Mofolo'nun çözümü ile ilgili tek sorun, Chrome'da IcanDivideBy0'ınkinden yaklaşık 30 kat daha yavaş olmasıdır. Se Gözat cevap aşağıda.
Bazıları

6

Ölçütlere ve ölçütlere karşılık gelen işleve sahip özel bir nesne oluşturabilirsiniz

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

Bu durumlarda ne yapmak istediğinize ilişkin işlevleri tanımlayın (function1, function2 vb. Tanımlayın)

Ve kuralları "değerlendirin"

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

Not

30 if ifadesi kullanmaktan nefret ediyorum

Birçok kez ifadelerin okunması ve bakımı daha kolaysa. Yukarıdakileri sadece gelecekte çok fazla koşulunuz ve çok fazla büyüme olasılığınız olduğunda tavsiye ederim .

Güncelleme
As @Brad yeterli olmalıdır üst sınır kontrol koşulları birbirini dışlayan ise (sadece bir tanesi bir seferde doğru olabilir), Açıklamalarda belirttiği:

if(scrollLeft < oneRule.upperLimit)

Resim koşullar artan tanımlanan bu (birinci düşük bir, 0 to 1000ve daha sonra 1000 to 2000örneğin)


action=function1- bunlar iki nokta üst üste olmamalı mı? ;-) - Ayrıca, eleme sürecinden dolayı, iki gruba giremeyeceğiniz için (sadece birden fazla eylemi mümkün kılmak için) iki üst gruba düşemeyeceğiniz için bunu sadece bir üst limite sahip olacak şekilde yeniden düzenleyebilirsiniz.
Brad Christie

Bra Christie Of Course
Nivas

@Brad, hayır bu benim niyetim değildi ve haklısın, üst sınır yeterli olmalı. Bunu güncelleme olarak ekleyeceğim ...
Nivas

Ben bu özlü ve temiz +1 bulmak
pimvdb

3

Tam olarak ne yapıyorsun //do stuff?

Şöyle bir şey yapabilirsiniz:

(scrollLeft < 1000) ? //do stuff
: (scrollLeft > 1000 && scrollLeft < 2000) ? //do stuff
: (scrollLeft > 2000) ? //do stuff
: //etc. 

3

Test edilmemiş ve bu işe yarayıp yaramayacağından emin değilsiniz, ama neden daha if statementsönce birkaçını yapmıyorsunuz 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

Bu başka bir seçenektir:

     switch (true) {
         case (value > 100):
             //do stuff
             break;
         case (value <= 100)&&(value > 75):
             //do stuff
             break;
         case (value < 50):
            //do stuff
             break;
     }

1

Kabul edilen cevabı güncelleme (henüz yorum yapamıyorum). 1/12/16 itibariyle demo jsfiddle'ı kromda kullanarak, switch-instant en hızlı çözümdür.

Sonuçlar: Zaman çözünürlüğü: 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

bitmiş

 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

gerçekten bağlıdır - 15ms "if-anında" 15ms "if-dolaylı" 15ms "anahtar-anında" 37ms "anahtar aralığı" 28ms "anahtar aralığı2" 35ms "anahtar-dolaylı dizi" 29ms "dizi-doğrusal anahtar" 62ms "dizi-ikili-anahtar" Bitirilmiş 1.00 (15ms) bitmişse 1.00 (15ms) bitirilirse 1.00 (15ms) anahtar-anında 2.47 (37ms) anahtar aralığı 1.87 (28ms) anahtar aralığı2 2.33 (35ms) anahtar- dolaylı dizi 1,93 (29 ms) dizi doğrusal anahtar 4.13 (62 ms) dizi ikili anahtar krom Sürüm 48.0.2564.109 (64 bit) mac os x 10.11.3
RenaissanceProgrammer

Mac OS x üzerinde ATM Safari 9.X ve Safari ios 9.3, "if-instant" açık bir galip
RenaissanceProgrammer

1
1 ms fark umurumda değil. Her test çalışmasından farklıdır. Önemli olan mantıklı olan kodlama stilini kullanın ve mikro optimizasyon yapmaya çalışmayın.
bazı

1

Benim durumumda (renk kodlaması bir yüzde, performans açısından kritik bir şey yok), hızlı bir şekilde şunu yazdım:

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 ifadesi kullanmaktan nefret ediyorum

Son zamanlarda aynı durumu yaşadım, bu şekilde çözdüm:

önce:

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

sonra:

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

Ve "1, 2, 3, 4, 5" ayarladıysanız, daha da basit olabilir:

var scales = [4, 6, 9, 12, 18];
scales.forEach(function(el){if(wind_speed >= el) scale++});
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.