Chuyển đổi câu lệnh cho lớn hơn / ít hơn


230

vì vậy tôi muốn sử dụng một câu lệnh chuyển đổi như thế này:

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

Bây giờ tôi biết rằng một trong những câu lệnh đó ( <1000) hoặc ( >1000 && <2000) sẽ không hoạt động (vì những lý do khác nhau, rõ ràng). Những gì tôi đang hỏi là cách hiệu quả nhất để làm điều đó. Tôi ghét sử dụng 30 ifcâu lệnh, vì vậy tôi muốn sử dụng cú pháp chuyển đổi. Có điều gì tôi có thể làm được không?


5
bước của bạn có thường xuyên không? Ý tôi là, nếu bạn chia scrollLeft cho 1000, bạn có thể chuyển 1, 2, 3 ...
IcanDivideBy0

Có lẽ bạn có thể tạo một mảng được sắp xếp ánh xạ một phạm vi điều kiện với thao tác tương ứng và áp dụng tìm kiếm nhị phân trên đó. Hoặc nếu điều kiện của bạn đủ thường xuyên, bạn có thể gọi trực tiếp your_mapper_object[scrollLeft / SOME_CONST], giả sử your_mapper_objectlà một cái gì đó như thế {1: some_func, 2: another_func, ...}. Và trong trường hợp này bạn cũng có thể sử dụng chuyển đổi.
Overmind Jiang

Câu trả lời:


731

Khi tôi nhìn vào các giải pháp trong các câu trả lời khác, tôi thấy một số điều mà tôi biết là không tốt cho hiệu suất. Tôi sẽ đưa chúng vào một bình luận nhưng tôi nghĩ sẽ tốt hơn nếu đánh giá nó và chia sẻ kết quả. Bạn có thể tự kiểm tra nó . Dưới đây là kết quả của tôi (ymmv) được chuẩn hóa sau thao tác nhanh nhất trong mỗi trình duyệt (nhân thời gian 1.0 với giá trị chuẩn hóa để có thời gian tuyệt đối tính bằng ms).

                    Chrome Firefox Opera MSIE Safari Nút
-------------------------------------------------- -----------------
1,0 thời gian 37ms 73ms 68ms 184ms 73ms 21ms
if-ngay 1.0 1.0 1.0 2.6 1.0 1.0
if-gián tiếp 1.2 1.8 3.3 3.8 2.6 1.0
chuyển đổi ngay lập tức 2.0 1.1 2.0 1.0 2.8 1.3
phạm vi chuyển đổi 38.1 10.6 2.6 7.3 20.9 10.4
chuyển đổi phạm vi2 31.9 8.3 2.0 4.5 9.5 6.9
chuyển mạch-gián tiếp-mảng 35.2 9.6 4.2 5.5 10.7 8.6
mảng-tuyến tính-chuyển đổi 3.6 4.1 4.5 10.0 4.7 2.7
mảng-nhị phân-chuyển đổi 7.8 6.7 9.5 16.0 15.0 4.9

Kiểm tra nơi thực hiện trên Windows 7 32 bit với các phiên bản folowing: Chrome 21.0.1180.89m , Firefox 15.0 , Opera 12.02 , MSIE 9.0.8112 , Safari 5.1.7 . Node được chạy trên hộp Linux 64 bit vì độ phân giải hẹn giờ trên Node.js cho Windows là 10ms thay vì 1ms.

nếu ngay lập tức

Đây là tốc độ nhanh nhất trong tất cả các môi trường được thử nghiệm, ngoại trừ trong ... MSIE trống ! (ngạc nhiên, ngạc nhiên). Đây là cách được khuyến nghị để thực hiện nó.

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

nếu-gián tiếp

Đây là một biến thể của switch-indirect-arraynhưng với if-statements thay vào đó và thực hiện nhanh hơn nhiều so với switch-indirect-arrayhầu hết các môi trường được thử nghiệm.

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

chuyển đổi ngay lập tức

Điều này khá nhanh trong tất cả các môi trường được thử nghiệm và thực sự là nhanh nhất trong MSIE. Nó hoạt động khi bạn có thể thực hiện một phép tính để lấy chỉ mục.

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

phạm vi chuyển đổi

Tốc độ này chậm hơn khoảng 6 đến 40 lần so với tốc độ nhanh nhất trong tất cả các môi trường được thử nghiệm ngoại trừ Opera, nơi nó mất khoảng một lần rưỡi thời gian. Nó chậm vì động cơ phải so sánh giá trị hai lần cho mỗi trường hợp. Đáng ngạc nhiên là Chrome phải mất gần 40 lần để hoàn thành việc này so với thao tác nhanh nhất trong Chrome, trong khi MSIE chỉ mất 6 lần thời gian. Nhưng chênh lệch thời gian thực tế chỉ là 74ms nghiêng về MSIE ở mức 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;
}

chuyển đổi phạm vi2

Đây là một biến thể của switch-rangenhưng chỉ có một so sánh cho mỗi trường hợp và do đó nhanh hơn, nhưng vẫn rất chậm, ngoại trừ trong Opera. Thứ tự của câu lệnh tình huống rất quan trọng vì động cơ sẽ kiểm tra từng trường hợp theo thứ tự mã nguồn ECMAScript262: 5 12.11

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

chuyển-gián tiếp-mảng

Trong biến thể này, các phạm vi được lưu trữ trong một mảng. Điều này chậm trong tất cả các môi trường được thử nghiệm và rất chậm trong 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;
}

mảng tuyến tính-tìm kiếm

Đây là sự kết hợp của tìm kiếm tuyến tính các giá trị trong một mảng và câu lệnh switch với các giá trị cố định. Lý do người ta có thể muốn sử dụng điều này là khi các giá trị không được biết cho đến khi chạy. Nó chậm trong mọi môi trường được thử nghiệm và mất gần 10 lần trong 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;
}

mảng nhị phân-chuyển đổi

Đây là một biến thể của array-linear-switchnhưng với một tìm kiếm nhị phân. Thật không may, nó chậm hơn so với tìm kiếm tuyến tính. Tôi không biết nếu đó là triển khai của tôi hoặc nếu tìm kiếm tuyến tính được tối ưu hóa hơn. Nó cũng có thể là không gian phím là nhỏ.

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

Phần kết luận

Nếu hiệu suất là quan trọng, sử dụng if-statements hoặc switchvới các giá trị ngay lập tức.


128
Thật hiếm khi thấy một câu trả lời với nhiều chi tiết và cấu trúc gọn gàng này. Lớn +1
Rick Donohoe

10
+1 lớn cho lời giải thích về mặt hiệu suất của vấn đề này!
Zoltán Schmidt

16
Đây là lý do stackoverflow là một trong những nơi tốt nhất cho câu trả lời. Đây là một câu trả lời "vượt thời gian", công việc tuyệt vời và cảm ơn cho jsfiddle!
Jessy

1
thông tin và giải thích về grt
JayKandari

3
Tôi thực sự muốn tôi có thể +2, một câu trả lời chi tiết như vậy!
Kaspar Lee

96

Một thay thế:

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

Bản trình diễn: http://jsfiddle.net/UWYzr/


4
đây là một giải pháp có giá trị hơn +1
IcanDivideBy0

1
Đây không phải là giống như if(...) else if(...)? Điều này tránh ifnhưng không hoàn toàn giống như một sự thay thế đẹp cho tôi.
pimvdb

7
Trong khi thanh lịch để mã, nó làm tổn thương hiệu suất. Chrome chậm hơn gần 30 lần so với sử dụng if-statements. Xem câu trả lời
một số

1
Tuy nhiên, hình phạt hiệu suất như vậy là không đáng kể khi dữ liệu được xử lý không lớn và có thể chức năng của nó chỉ được áp dụng, như xác nhận một đầu vào của người dùng, sau đó khả năng đọc được chọn thay vì hiệu suất trong trường hợp đó.
Jesús Franco

1
Điều này thật đúng với gì mà tôi đã tìm kiếm. Cảm ơn!
Ami Schreiber

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

Chỉ hoạt động nếu bạn có các bước thông thường ...

EDIT: vì giải pháp này tiếp tục được nâng cấp, tôi phải khuyên rằng giải pháp của mofolo là cách tốt hơn


1
Tôi đã sử dụng Math.round(scrollLeft/1000)bằng cách này.
switz

@Switz - Hãy nhớ rằng 999 <1000 rơi vào trường hợp 0 ​​nhưng Math.round (999/1000) rơi vào trường hợp 1. Ngoài ra, có một lỗi đánh máy ở trên, trong trường hợp đó là 1 = = 1000, không chỉ> 1000 .
Igor

Vấn đề duy nhất với giải pháp của mofolo là Chrome chậm hơn khoảng 30 lần so với giải pháp của IcanDivideBy0. Se câu trả lời của tôi dưới đây.
một số

6

Bạn có thể tạo một đối tượng tùy chỉnh với tiêu chí và hàm tương ứng với tiêu chí

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

Xác định các hàm cho những gì bạn muốn làm trong những trường hợp này (xác định hàm1, function2, v.v.)

Và "đánh giá" các quy tắc

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

Ghi chú

Tôi ghét sử dụng 30 nếu báo cáo

Nhiều lần nếu các câu lệnh dễ đọc và duy trì hơn. Tôi muốn giới thiệu ở trên chỉ khi bạn có rất nhiều điều kiện khả năng của nhiều tăng trưởng trong tương lai.

Cập nhật
Như @Brad đã chỉ ra trong các nhận xét, nếu các điều kiện loại trừ lẫn nhau (chỉ một trong số chúng có thể đúng tại một thời điểm), kiểm tra giới hạn trên là đủ:

if(scrollLeft < oneRule.upperLimit)

cung cấp các điều kiện được quy định theo thứ tự tăng dần (đầu tiên thấp nhất, 0 to 1000và sau đó 1000 to 2000chẳng hạn)


action=function1- không nên là dấu hai chấm? ;-) - Bạn cũng có thể cấu trúc lại điều này để chỉ có giới hạn trên vì, do quá trình loại bỏ, bạn không thể nằm trong hai nhóm - trừ khi đó là ý định của bạn (có thể có nhiều hành động).
Brad Christie

@Brad Christie Tất nhiên
Nivas

@Brad, không, đó không phải là ý định của tôi, và bạn nói đúng, giới hạn trên sẽ đủ. Sẽ thêm vào đó như một bản cập nhật ...
Nivas

Tôi tìm thấy cái này ngắn gọn và gọn gàng +1
pimvdb

3

Chính xác thì bạn đang làm gì //do stuff?

Bạn có thể làm một cái gì đó như:

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

3

Chưa được kiểm tra và không chắc chắn nếu điều này sẽ hoạt động, nhưng tại sao không làm một vài điều if statementstrước đó, để đặt các biến cho 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

Đây là một lựa chọn khác:

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

1

Cập nhật câu trả lời được chấp nhận (chưa thể bình luận). Kể từ 1/12/16 bằng cách sử dụng bản demo jsfiddle trong chrome, chuyển đổi ngay lập tức là giải pháp nhanh nhất.

Kết quả: Thời gian giải quyết: 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

Đã kết thúc

 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

nó thực sự phụ thuộc - 15ms "if-liền" 15ms "if-gián tiếp" 15ms "switch-ngay" 37ms "switch-Range" 28ms "switch-Range2" 35ms "switch-gián tiếp mảng" 29ms "mảng-linear-switch" 62ms "chuyển đổi mảng nhị phân" Hoàn thành 1,00 (15ms) if-liền 1,00 (15ms) if-gián tiếp 1,00 (15ms) chuyển đổi ngay lập tức 2,47 (37ms) chuyển đổi phạm vi 1,87 (28ms) chuyển đổi phạm vi2 2,33 (35ms) mảng gián tiếp 1,93 (29ms) mảng chuyển đổi tuyến tính mảng 4,13 (62ms) chrome phiên bản 48.0.2564.109 (64-bit) mac os x 10.11.3
RenaissanceProgrammer

ATM Safari 9.X trên Mac OS x và Safari ios 9.3, "nếu ngay lập tức" là người chiến thắng rõ ràng
RenaissanceProgrammer

1
Sự khác biệt 1 ms là quá ít để quan tâm. Nó thay đổi nhiều hơn thế từ mỗi lần chạy thử. Vấn đề là: Sử dụng kiểu mã hóa có ý nghĩa và đừng cố tối ưu hóa vi mô.
một số

1

Trong trường hợp của tôi (mã hóa màu theo tỷ lệ phần trăm, không có gì quan trọng về hiệu năng), tôi đã nhanh chóng viết điều này:

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

Tôi ghét sử dụng 30 nếu báo cáo

Gần đây tôi đã gặp tình huống tương tự, đó là cách tôi giải quyết nó:

trước:

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

sau:

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

Và nếu bạn đặt "1, 2, 3, 4, 5" thì có thể đơn giản hơn nữa:

var scales = [4, 6, 9, 12, 18];
scales.forEach(function(el){if(wind_speed >= el) scale++});
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.