条件付きステートメントを短縮する方法


154

次のような非常に長い条件文があります。

if(test.type == 'itema' || test.type == 'itemb' || test.type == 'itemc' || test.type == 'itemd'){
    // do something.
}

この表現/ステートメントをより簡潔な形式にリファクタリングできるかどうか疑問に思っていました。

これを達成する方法について何か考えはありますか?


23
それらを配列に入れて使用できinますか?
jeremy 2013


誰かがどれが最も速いかを確認できた場合にのみ、今度は
ムハンマドウメル2013

3
これはおそらく誰にとってもショックですが、OPが持っているのは明らかにスピードの勝者です!!!!!!! たぶん、ブラウザがこれのために多く最適化するかもしれません。結果:(1)の場合||。(2)switchステートメント。(3)正規表現。(4)~jsperf.com/if-statements-test-techsin
Muhammad

3
また、これに間違った方法でアプローチしている可能性もあります。この場合、これらの4つのタイプには共通点があります。それは何ですか?これをさらに極端なケースに当てはめると、この条件に一致するようにさらに10の型を追加する必要がある場合はどうなるでしょうか。それとも100?それ以上ある場合は、おそらくこのソリューションや他の提案されたソリューションの使用を検討しないでしょう。このような大きなifステートメントがあり、それがコードのにおいだと思っているのは良い兆候です。これをより簡潔にする最良の方法は、if(test.your_common_condition)と書ける場合です。このコンテキストでは理解しやすく、より拡張可能です。
gmacdougall 2013

回答:


241

値を配列に入れ、項目が配列にあるかどうかを確認します。

if ([1, 2, 3, 4].includes(test.type)) {
    // Do something
}

サポートしているブラウザにArray#includesメソッドがない場合は、このポリフィルを使用できます。


~チルダショートカットの簡単な説明:

アップデート:私たちが今持っているのでincludes方法を、使用にはポイントがない~ハックはもう。それがどのように機能するかを知ることに興味がある、および/または他のコードでそれに遭遇した人のために、これをここに保持してください。

代わりに結果がかどうかをチェックするのindexOf>= 0、ちょっといいのショートカットがあります:

if ( ~[1, 2, 3, 4].indexOf(test.type) ) {
    // Do something
}

ここにフィドルがあります:http : //jsfiddle.net/HYJvK/

これはどのように作動しますか?配列でアイテムが見つかった場合は、indexOfそのインデックスを返します。アイテムが見つからなかった場合は、が返され-1ます。詳細に触れずに、~ビットごとのNOT演算子であり、0に対してのみ返され-1ます。

~戻り値を比較するよりも簡潔なので、ショートカットを使用するのが好きです。JavaScriptにin_arrayブール値を直接返す関数(PHPと同様)があればいいのにと思っていますが、それはあくまでも希望的なものです(更新:現在はそうです。と呼ばれていincludesます。上記参照)。jQueryはinArray、PHPのメソッドシグネチャを共有しながら、実際にはネイティブindexOf機能を模倣していることに注意してください(これは、インデックスが本当に必要な場合に、さまざまなケースで役立ちます)。

重要な注意:ティルデショートカットを使用すると、コードが十分に明確ではなく、すべての犠牲を払って回避すべきであると激しく信じているため、論争の渦中になっているようです(この回答のコメントを参照)。あなたが彼らの感情を共有するなら、あなたは.indexOf(...) >= 0解決策に固執するべきです。


もう少し長い説明:

JavaScriptの整数は符号付きです。つまり、左端のビットは符号ビットとして予約されています。数値が正であるか負であるかを示すフラグ1

32ビットのバイナリ形式の正の数の例を次に示します。

1 :    00000000000000000000000000000001
2 :    00000000000000000000000000000010
3 :    00000000000000000000000000000011
15:    00000000000000000000000000001111

ここに同じ数字がありますが、負です:

-1 :   11111111111111111111111111111111
-2 :   11111111111111111111111111111110
-3 :   11111111111111111111111111111101
-15:   11111111111111111111111111110001

なぜ負の数のこのような奇妙な組み合わせですか?シンプル。負の数は正の数の逆数+ 1です。負の数を正の数に加算すると、常にが生成され0ます。

これを理解するために、簡単な2進算術を実行してみましょう。

以下に追加-1する方法を示します+1

   00000000000000000000000000000001      +1
+  11111111111111111111111111111111      -1
-------------------------------------------
=  00000000000000000000000000000000       0

そして、これは私たちがどのように追加-15するか+15です:

   00000000000000000000000000001111      +15
+  11111111111111111111111111110001      -15
--------------------------------------------
=  00000000000000000000000000000000        0

どのようにしてそれらの結果を得るのですか?定期的に足し算を行うことで、学校で教えられたように、一番右の列から始めて、すべての行を足します。合計が最大の1桁の数値(10進数では9ですが、2進数では1)より大きい場合、残りを次の列に繰り越します。

ここで、お気づきのように、負の数を正の数に追加すると、すべて0のであるとは限らない右端の列には常に2つ1のがあり、合計するとになり2ます。2のバイナリ表現はなので、次の列10にを運び、結果の1a 0を最初の列に入れます。左にある他のすべての列には、行が1つだけある1ため、1前の列から繰り越された値は再び合計され2、次に繰り越されます...このプロセスは、左端の列に到達するまで繰り返されます。1引き継がれるために、それがオーバーフローすると失われるので、行き場がない、と私たちはを残している0すべてにわたって秒。

このシステムは2の補数と呼ばれます。あなたはここでこれについてもっと読むことができます:

符号付き整数の2の補数表現


2の補数のクラッシュコース-1が終了したので、2進数表現が1全体に渡る唯一の数値であることがわかります。

~ビット単位のNOT演算子を使用すると、指定された数値のすべてのビットが反転されます。0すべてのビットを反転させることから戻る唯一の方法は、すべてので始めた場合1です。

だから、このすべてはそれが言っての長ったらしい方法だった~nだけ返され0た場合nです-1


59
ビットワイズ演算子を使用することは確かに魅力!== -1的ですが、考えられるどの方法よりも本当に優れていますか?ゼロの偽り性を暗黙的に使用するよりも、明示的なブールロジックの方が適切ではないでしょうか。
フィル

21
巧妙に巧妙ですが、私はそれが好きではありません。一見コードが何をしているのかが一目瞭然ではないため、メンテナンスが困難です。私は「ユーリー・ギャランター」の答えをとても気に入っています。
Jon Rea 2013

65
-1人の新しいプログラマーがこのような答えを見て、それがクールで許容できるコーディング方法であると考え、5年以内に彼らのコードを維持して私の髪を
引き裂かなければなり

23
この慣用句は、私の専門分野であるC#、Java、Pythonなどの言語では一般的ではありません。そして、私はここで数人の地元のJavaScriptエキスパートに尋ねたところ、誰もそれが以前に行われたのを見たことがない。そのため、あなたが主張するほど明らかに一般的ではありません。 より明確で一般的なものを優先して、常に回避する必要があります!= -1
BlueRaja-Danny Pflughoeft 2013

12
-1は、そもそもビット表現について多くのことを実際に指定していない言語での不必要なビットフィドルによるものです。さらに、この答えの大部分はビットフィドリングを説明しています。ハックを説明するために20段落を書く必要がある場合、本当に時間を節約できますか?
2013

242

フォールスルーでswitchステートメントを使用できます:

switch (test.type) {

  case "itema":
  case "itemb":
  case "itemc":
  case "itemd":
    // do something
}

9
基本的にはifと同じですが、配列メソッドのインデックスははるかに優れています
NimChimpsky 2013

6
@kojiroその悲しいことに、正しい答えはこれですが、素晴らしいbitwhise-arrayトリックの代わりにこれに注意を向けることは不可能です。
Manu343726 2013

1
私はこの答えがここになければならないことを知っていましたが、それを見つけるには、一番下までスクロールする必要がありました。それこそが、switchステートメントが設計された目的であり、他の多くの言語にも継承されます。switchステートメントの「フォールスルー」メソッドについて多くの人が知らないことがわかりました。
ジミージョンソン

3
このソリューションは、FirefoxとSafariで最速で||、Chromeで(元のに次いで)2番目に高速です。jsperf.com/if-statements-test-techsinを
pabouk

3
これは、多くの条件がある場合にメソッドで記述するのはひどいと思います...いくつかの条件で問題ない場合は、10以上の場合、配列でコードを適切に保つようにします。
yu_ominae 2013

63

サイエンスを使用する:コードを短くして、idfahが言ったこととこれを最速で実行する必要があります。

これは~メソッドよりも高速です

var x = test.type;
if (x == 'itema' ||
    x == 'itemb' ||
    x == 'itemc' ||
    x == 'itemd') {
    //do something
}

http://jsperf.com/if-statements-test-techsin ここに画像の説明を入力してください (上部セット:Chrome、下部セット:Firefox)

結論:

場合は可能性があり、いくつかの、あなたはその特定のものは、あなたが最大のパフォーマンスを出すよりも、発生する可能性が高くなります知っているif ||switch fall throughif(obj[keyval])

可能性が多く、そのいずれかが最も発生している可能性がある場合、つまり、オブジェクトルックアップのパフォーマンスを最大限に発揮しif(obj[keyval])regexそれが当てはまる場合よりも、どちらが発生する可能性が高いかを知ることはできません。

http://jsperf.com/if-statements-test-techsin/12

何か新しいものが出てきたらアップデートします。


2
本当に良い投稿のための+1!私が正しく理解している場合、これswitch caseが最速の方法ですか?
user1477388 2013

1
Firefoxではい、そのクロム中if ( ...||...||...)...
ムハンマドUmer

8
この入力に対して実際に多くのループを実行する場合はより高速ですが、非常に大きなn( "itemX"文字列の数)を持つ1つのループを使用する場合ははるかに低速です。検証(またはおそらく反駁)に使用できるこのコードジェネレーターをハッキングしました。obj["itemX"]nが大きい場合は非常に高速です。基本的に、高速なものはコンテキストに依存します。楽しんで。
kojiro 2013

3
それが最速の方法ですが、重要ですか?
congusbongus 2013

1
@Mich速度のためだけにコードの優雅さを犠牲にしないでください。これは多くの人々があなたに言うでしょう。結局、常識を働かせてください。
Andre Figueiredo

32

文字列と比較していてパターンがある場合は、正規表現の使用を検討してください。

それ以外の場合は、短縮しようとするとコードが難読化されるだけだと思います。単に行を折り返してきれいにすることを検討してください。

if (test.type == 'itema' ||
    test.type == 'itemb' ||
    test.type == 'itemc' ||
    test.type == 'itemd') {
    do something.
}

4
この答えは、速度の点で勝者です jsperf.com/if-statements-test-techsin
Muhammad

1
これは、プロジェクトがメンテナンスモードに入るときに(たとえば、などのルールを使用して(test.type == 'itemf' && foo.mode == 'detailed'))拡張するのも最も簡単です
イズカタ2013

16
var possibilities = {
  "itema": 1,
  "itemb": 1,
  "itemc": 1,
…};
if (test.type in possibilities) {  }

オブジェクトを連想配列として使用することはごく一般的なことですが、JavaScriptにはネイティブセットがないため、オブジェクトを安価なセットとして使用することもできます。


これは、FlyingCatが短くしようとしているというifステートメントの通常よりも短いのですか?
dcarson 2013

1
@dcarson OPのifステートメントの条件は、空白をすべて削除すると78文字になります。次のように書くと、鉱山は54になりますtest.type in {"itema":1,"itemb":1,"itemc":1,"itemd":1}。基本的に、彼は追加のキーごとに2つ使用するごとに4文字を使用します。
kojiro 2013

1
しかし、あなたはできる:if(possibilities [test.type])そして全体の2文字を節約する!:)
dc5 2013

15
if( /^item[a-d]$/.test(test.type) ) { /* do something */ }

または、アイテムが均一でない場合は、次のようになります。

if( /^(itema|itemb|itemc|itemd)$/.test(test.type) ) { /* do something */ }

9
「問題に直面したときに、「わかっている、正規表現を使用する」と考える人もいます。今、彼らには二つの問題があります。」-ジェイミー・ザウィンスキー、1997年
モシェ・カッツ

5
@MosheKatz私は人々がその引用についてはばかげているのが好きだと理解することができます-そして人々は確かに完全に不適切なもののために正規表現を使用しますが、これはそれらの1つではありません。OPが提供するケースでは、これは基準に一致するだけでなく、非常によく一致します。正規表現は本質的に邪悪なものではなく、明確に定義されたパラメーターを持つ文字列のマッチングは、そのために作成されたものです。
Thor84no 2013

3
@ Thor84no通常、私は質問者が最初のケースのような不自然な例と実際に一致しようと試みておらず、現実世界の一致がそれほど単純ではないと想定します。それを行う正しい方法であること。言い換えると、RegExがパイプ文字で区切られたオプションのリストにすぎない場合、他のどの提案よりも読みやすく、効率が大幅に低下する可能性があります。
Moshe Katz

10

すばらしい答えですが、関数の中にそれらをラップすることで、コードをはるかに読みやすくすることができます。

これは複雑なifステートメントであり、あなた(または他の誰か)が数年かけてコードを読んだとき、何が起こっているのかを理解するためのセクションを見つけるためにスキャンすることになります。このレベルのビジネスロジックを使用したステートメントでは、テストしていることを理解している間、数秒間つまずきます。このようなコードでは、スキャンを続行できます。

if(CheckIfBusinessRuleIsTrue())
{
    //Do Something
}

function CheckIfBusinessRuleIsTrue() 
{
    return (the best solution from previous posts here);
}

関数に明示的に名前を付けると、何をテストしているかがすぐにわかり、コードをスキャンして理解しやすくなります。


1
私がここで見た最良の答え。本当に、人々は良いデザインの原則を気にしていないようです。何かをすばやく修正し、システムを将来的に保守できるようにコードを改善することを忘れたいだけです!
Maykonn 2013

のようにコメントするのは// CheckIfBusinessRuleIsTrueどうですか?
daniel1426 2014年

4

すべての回答をJavascriptセットに入れて、そのセットを呼び出すだけ.contains()です。

すべてのコンテンツを宣言する必要がありますが、インライン呼び出しは短くなります。

何かのようなもの:

var itemSet = new Set(["itema","itemb","itemc","itemd"]);
if( itemSet.contains( test.type ){}

4
これは、OPが実行しようとしていることを達成するための非常に無駄な方法のようです。あなたがしながら、そうでき、余分なサードパーティのライブラリを含めるオブジェクトをインスタンス化し、その上に呼び出し方法、あなたはおそらくいけません。
KaptajnKold 2013

@Captain Cold:まあ、OPはメモリフットプリントではなく簡潔さを求めました。おそらく、セットは他の操作に再利用できますか?
グイドアンセルミ2013

1
確かですが、そうであっても:正直なところ、これを自分行うことはありますか?これを実際に目にしたことがあれば、それを主要なWTFと見なします。
KaptajnKold 2013

1
ええ、あなたは正しいです(私はあなたに+1を与えました)が、このチェックは他のどこでも行われていないと想定しています。他のいくつかの場所で行われている場合や、テストの変更が行われている場合は、セットを使用することには意味があります。OPに任せて、最適なソリューションを選択します。これが孤独な使用法であれば、セットを使用するのは恥ずかしがり屋の帽子に値することに同意します。
グイドアンセルミ2013

2
私は実際、選択された答えは絶対にひどいもので、Setを使用するよりも悪いと思います。
グイドアンセルミ2013

2

これを実現する私のお気に入りの方法の1つは、underscore.jsなどのライブラリを使用することです...

var isItem = _.some(['itema','itemb','itemc','itemd'], function(item) {
    return test.type === item;
});

if(isItem) {
    // One of them was true
}

http://underscorejs.org/#some


1
contains間違いなくsome
Dennis

1
このためにライブラリを使用する必要はありませんsome。EC5のArrayプロトタイプの関数です。
KaptajnKold 2013

2
確かに、すべての人がEC5サポートを利用できるわけではありません。さらに、私はアンダースコアが本当に好きです。:)
jcreamer898 2013

場合は、あなたがしているすでにアンダースコアのようなライブラリを使用して、これはおそらく最も簡単な方法です。それ以外の場合は、1つの関数だけのためにライブラリ全体をロードすることはほとんど意味がありません。
Moshe Katz

2

別の方法または私が見つけた別の素晴らしい方法はこれです...

if ('a' in oc(['a','b','c'])) { //dosomething }

function oc(a)
{
  var o = {};
  for(var i=0;i<a.length;i++)  o[a[i]]='';
  return o;
}

もちろん、これを見るとわかるように、物事はさらに一歩進んで、ロジックに従いやすくなっています。

http://snook.ca/archives/javascript/testing_for_a_v

〜&& ||などの演算子を使用する (()、())~~は、コードが後で中断する場合にのみ問題ありません。どこから始めればいいかわからない。したがって、読みやすさは非常に重要です。

必要な場合は短くすることができます。

('a' in oc(['a','b','c'])) && statement;
('a' in oc(['a','b','c'])) && (statements,statements);
('a' in oc(['a','b','c']))?statement:elseStatement;
('a' in oc(['a','b','c']))?(statements,statements):(elseStatements,elseStatements);

逆にしたい場合

('a' in oc(['a','b','c'])) || statement;

2

switchステートメントの代わりにifステートメントを使用するだけです。

switch (test.type) {

  case "itema":case "itemb":case "itemc":case "itemd":
    // do your process
  case "other cases":...:
    // do other processes
  default:
    // do processes when test.type does not meet your predictions.
}

Switch 内の多くの条件を比較するよりも速く動作します if


2

文字列の非常に長いリストの場合、このアイデアはいくつかの文字を節約します(実際に推奨するわけではありませんが、機能するはずです)。

test.typeに含まれないことがわかっている文字を選択し、それを区切り文字として使用して、すべてを1つの長い文字列に挿入し、それを検索します。

if ("/itema/itemb/itemc/itemd/".indexOf("/"+test.type+"/")>=0) {
  // doSomething
}

文字列がさらに制約されている場合は、区切り文字を省略することもできます...

if ("itemaitembitemcitemd".indexOf(test.type)>=0) {
  // doSomething
}

...しかし、その場合は誤検知に注意する必要があります(たとえば、「embite」はそのバージョンで一致します)。


2

読みやすくするために、テスト用の関数を作成します(はい、1行の関数)。

function isTypeDefined(test) {
    return test.type == 'itema' ||
           test.type == 'itemb' ||
           test.type == 'itemc' ||
           test.type == 'itemd';
}

それを呼び出す:


    if (isTypeDefined(test)) {

}
...

1

このようなif条件を書く場合、2つの目的があると思います。

  1. 簡潔
  2. 読みやすさ

そのため、場合によっては#1が最速かもしれませんが、後で簡単にメンテナンスできるように#2を使用します。シナリオによっては、ウォルターの回答のバリエーションを選択することがよくあります。

まず、既存のライブラリの一部として、グローバルに利用できる関数を用意します。

function isDefined(obj){
  return (typeof(obj) != 'undefined');
}

次に、実際にif条件を実行したい場合は、有効な値のリストを含むオブジェクトを作成します。

var validOptions = {
  "itema":1,
  "itemb":1,
  "itemc":1,
  "itemd":1
};
if(isDefined(validOptions[test.type])){
  //do something...
}

これは、switch / caseステートメントほど高速ではなく、他のいくつかの例よりも少し冗長ですが、コード内の他の場所でオブジェクトを再利用することがよくあります。

上記で作成したjsperfサンプルの1つをピギーバックして、速度を比較するためにこのテストとバリエーションを追加しました。http://jsperf.com/if-statements-test-techsin/6最も興味深いのは、Firefoxの特定のテストコンボはChromeよりもはるかに高速であることです。


1

これは、単純なforループで解決できます。

test = {};
test.type = 'itema';

for(var i=['itema','itemb','itemc']; i[0]==test.type && [
    (function() {
        // do something
        console.log('matched!');
    })()
]; i.shift());

forループの最初のセクションを使用して照合する引数を初期化し、2番目のセクションを使用してforループの実行を停止し、3番目のセクションを使用して最終的にループを終了します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.