สลับคำสั่งสำหรับมากกว่า / น้อยกว่า


230

ดังนั้นฉันต้องการใช้คำสั่ง switch เช่นนี้

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

ตอนนี้ฉันรู้แล้วว่าข้อความเหล่านี้ ( <1000) หรือ ( >1000 && <2000) ไม่ทำงาน (ด้วยเหตุผลที่แตกต่างกันอย่างชัดเจน) สิ่งที่ฉันถามคือวิธีที่มีประสิทธิภาพที่สุดในการทำสิ่งนั้น ฉันเกลียดการใช้ 30 ifคำสั่งดังนั้นฉันควรใช้ไวยากรณ์สวิตช์ มีอะไรที่ฉันทำได้บ้างไหม?


5
ขั้นตอนของคุณเป็นปกติหรือไม่ ฉันหมายความว่าถ้าคุณหาร scrollLeft ด้วย 1,000 คุณสามารถสลับ 1, 2, 3 ...
IcanDivideBy0

บางทีคุณอาจสร้างอาร์เรย์ที่เรียงลำดับซึ่งจับคู่ช่วงเงื่อนไขกับการดำเนินการที่เกี่ยวข้องและใช้การค้นหาแบบไบนารีกับมัน หรือหากเงื่อนไขของคุณเป็นปกติมากพอคุณสามารถโทรได้โดยตรงyour_mapper_object[scrollLeft / SOME_CONST]โดยสมมติว่าyour_mapper_objectเป็นเช่น{1: some_func, 2: another_func, ...}นั้น และในกรณีนี้คุณสามารถใช้สวิตช์ได้
Overmind Jiang

คำตอบ:


731

เมื่อฉันดูวิธีแก้ปัญหาในคำตอบอื่น ๆ ฉันเห็นบางสิ่งที่ฉันรู้ว่าไม่ดีต่อประสิทธิภาพ ฉันจะใส่พวกเขาในความคิดเห็น แต่ฉันคิดว่ามันจะเป็นการดีกว่าที่จะทำการเปรียบเทียบและแบ่งปันผลลัพธ์ คุณสามารถทดสอบได้ด้วยตัวคุณเอง ด้านล่างคือผลลัพธ์ของฉัน (ymmv) ที่ทำให้เป็นมาตรฐานหลังจากการดำเนินการที่เร็วที่สุดในแต่ละเบราว์เซอร์ (คูณ 1.0 ครั้งด้วยค่าปกติเพื่อให้ได้เวลาสัมบูรณ์เป็น ms)

                    Chrome Firefox Opera MSIE Safari โหนด
-------------------------------------------------- -----------------
1.0 เวลา 37ms 73ms 68ms 184ms 73ms 21ms
if-ทันที 1.0 1.0 1.0 2.6 1.0 1.0
if-อ้อม 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
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
array-linear-switch 3.6 4.1 4.5 10.0 4.7 2.7
array-binary-switch 7.8 6.7 9.5 16.0 15.0 4.9

การทดสอบที่ดำเนินการบน Windows 7 32bit กับรุ่น folowing: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 โหนดถูกเรียกใช้บนกล่อง Linux 64 บิตเนื่องจากความละเอียดของตัวจับเวลาใน Node.js สำหรับ Windows คือ 10 มิลลิเซคอนแทนที่จะเป็น 1 มิลลิวินาที

ถ้า-ทันที

นี่เป็นวิธีที่เร็วที่สุดในทุกสภาพแวดล้อมที่ทดสอบยกเว้นใน ... drumroll MSIE! (ประหลาดใจประหลาดใจ) นี่เป็นวิธีที่แนะนำให้ใช้

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 ครั้งเท่านั้น แต่ความแตกต่างของเวลาที่แท้จริงนั้นอยู่ที่ 74ms สำหรับ MSIE ที่ 1337ms (!)

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 ลำดับของคำสั่ง 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;
}

อาร์เรย์เชิงเส้นค้นหา

นี่คือการรวมกันของการค้นหาเชิงเส้นของค่าในอาร์เรย์และคำสั่งสวิทช์ที่มีค่าคงที่ เหตุผลที่อาจต้องการใช้สิ่งนี้คือเมื่อไม่ทราบค่าจนกว่าจะถึงเวลารันไทม์ มันช้าในทุกสภาพแวดล้อมที่ทดสอบและใช้เวลาเกือบ 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แต่ด้วยการค้นหาแบบไบนารี น่าเสียดายที่มันช้ากว่าการค้นหาเชิงเส้น ฉันไม่รู้ว่าเป็นการใช้งานของฉันหรือไม่หรือการค้นหาแบบเชิงเส้นเหมาะสมที่สุด อาจเป็นไปได้ว่า keyspace มีขนาดเล็ก

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
Rick Donohoe

10
บิ๊ก +1 สำหรับคำอธิบายด้านประสิทธิภาพของปัญหานี้!
Zoltán Schmidt

16
นี่คือเหตุผลที่ stackoverflow เป็นหนึ่งในสถานที่ที่ดีที่สุดสำหรับคำตอบ นี่คือคำตอบ "อมตะ" งานที่ยอดเยี่ยมและขอบคุณสำหรับ jsfiddle!
Jessy

1
ข้อมูล grt & คำอธิบาย
JayKandari

3
ฉันหวังว่าฉันจะสามารถ +1 ได้คำตอบอย่างละเอียด!
Kaspar Lee

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
ในขณะที่รหัสสวยงามมันเจ็บประสิทธิภาพ Chrome ทำงานช้ากว่าการใช้if-statements เกือบ 30 เท่า ดูคำตอบ
บางส่วน

1
อย่างไรก็ตามการปรับประสิทธิภาพดังกล่าวมีความสำคัญน้อยมากเมื่อข้อมูลที่ได้รับการจัดการมีขนาดไม่ใหญ่นักและอาจเป็นฟังก์ชั่นที่ใช้เพียงอย่างเดียวเช่นการตรวจสอบความถูกต้องของอินพุตของผู้ใช้คนเดียว
Jesús Franco

1
นี่คือสิ่งที่ฉันกำลังมองหา ขอบคุณ!
Ami Schreiber

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

ใช้งานได้เฉพาะเมื่อคุณมีขั้นตอนปกติ ...

แก้ไข: เนื่องจากโซลูชั่นนี้ยังคงได้รับ upvotes ฉันต้องแนะนำว่าทางออกของ mofoloดีกว่า


1
ฉันใช้Math.round(scrollLeft/1000)ตามวิธี
switz

@Switz - โปรดจำไว้ว่า 999 <1,000 ตรงกับกรณีที่ 0 แต่ Math.round (999/1000) ตรงกับกรณีที่ 1 นอกจากนี้ยังมีการพิมพ์ผิดด้านบนในกรณีที่ 1 คือ> = 1,000 ไม่ใช่แค่> 1,000 .
อิกอร์

ปัญหาเดียวกับวิธีแก้ปัญหาของ 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 }];

กำหนดฟังก์ชั่นสำหรับสิ่งที่คุณต้องการทำในกรณีเหล่านี้ (กำหนด 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- เหล่านี้ไม่ควร colons? ;-) - คุณสามารถ refactor สิ่งนี้เพื่อให้มีขีด จำกัด สูงสุดเนื่องจากกระบวนการกำจัดคุณไม่สามารถตกอยู่ในสองกลุ่ม - ยกเว้นว่าเป็นความตั้งใจของคุณ
แบรดคริสตี้

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


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- ทางอ้อม" 15ms "สลับ - ทันที" 37ms "สลับช่วง" 28ms "สลับ-range2" 35ms "สลับ - อ้อม - อาร์เรย์" 29ms "อาร์เรย์ - เชิงเส้น - สวิตช์" 62ms "array-binary-switch" เสร็จสิ้น 1.00 (15ms) ถ้าทันที 1.00 (15ms) ถ้า-อ้อม 1.00 (15ms) สลับได้ทันที 2.47 (37ms) สลับ - ช่วง 1.87 (28ms) สลับ - ช่วงที่ 2.33 (35ms) อาร์เรย์ทางอ้อม 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

ATM Safari 9.X บน Mac OS x และ Safari ios 9.3“ ถ้าทันที” เป็นผู้ชนะที่ชัดเจน
RenaissanceProgrammer

1
ความแตกต่าง 1 ms น้อยเกินไปที่จะใส่ใจ มันแตกต่างมากกว่านั้นจากการทดสอบแต่ละครั้ง ประเด็นคือ: ใช้รูปแบบการเข้ารหัสที่สมเหตุสมผลและอย่าพยายามปรับให้เหมาะสมขนาดเล็ก
บาง

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++});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.