web-dev-qa-db-ja.com

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

次のような非常に長い条件ステートメントがあります。

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

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

これを達成する方法についてのアイデアはありますか?

153
FlyingCat

値を配列に入れ、アイテムが配列内にあるかどうかを確認します。

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

サポートするブラウザに Array#includes メソッドがない場合は、 this polyfill を使用できます。


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

更新:includesメソッドが用意されたので、~ハックを使用しても意味がありません。これがどのように機能するかを知りたい、および/または他のコードで遭遇した人のために、これをここに保管してください。

indexOfの結果が>= 0かどうかをチェックする代わりに、素敵な小さなショートカットがあります:

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

フィドルは次のとおりです。 http://jsfiddle.net/HYJvK/

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

~ショートカットを使用するのが好きです。戻り値を比較するよりも簡潔だからです。 JavaScriptにブール値を直接返すin_array関数があればいいのに(PHPに似ています)、それはただ希望的観測です(Update:今では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が生成されます。

これを理解するために、いくつかの簡単なバイナリー算術を行いましょう。

-1+1に追加する方法は次のとおりです。

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

そして、これが-15+15に追加する方法です:

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

これらの結果をどのように取得しますか?学校で教えられた方法で定期的に追加することにより、右端の列から始めて、すべての行を合計します。合計が最大の1桁の数値(10進数では9ですが、バイナリでは1)よりも大きい場合、残りを次の列に持ち越します。

さて、お気づきのとおり、負の数を正の数に追加すると、すべての0sではない右端の列には常に2つの1sがあり、一緒に追加すると2になります。 2つの2進数表現は10であるため、1を次の列に移動し、結果の0を最初の列に配置します。左にある他のすべての列には1を持つ行が1つしかないので、前の列から引き継がれた1が再び2に追加され、それが引き継がれます...カラムには、持ち越される1の行き先がないため、オーバーフローして失われ、すべての0sが残っています。

このシステムは2の補数と呼ばれます。詳細については、こちらをご覧ください。

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


2の補数でのクラッシュコースが終了したので、-1のみがバイナリ表現が1である唯一の数字であることに気付くでしょう。

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

そのため、これは、n~nである場合にのみ0-1を返すという長文の方法でした。

240
Joseph Silber

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

switch (test.type) {

  case "itema":
  case "itemb":
  case "itemc":
  case "itemd":
    // do something
}
242
Yuriy Galanter

Scienceの使用:idfahが言ったことを実行する必要があります。これは、コードを短くしながら最速にするためです。

THIS ISより速い_~_メソッド

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

http://jsperf.com/if-statements-test-techsin enter image description here (上部セット:Chrome、下部セット:Firefox)

結論:

If可能性はfewであり、特定のものは_if ||_、_switch fall through_、およびif(obj[keyval])で最大のパフォーマンスを得るよりも発生します。

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

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

何か新しいものが出てきたら更新します。

62
Muhammad Umer

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

そうしないと、コードを短くしようとすると、コードが難読化されるだけです。単純に行を折り返して見やすくすることを検討してください。

if (test.type == 'itema' ||
    test.type == 'itemb' ||
    test.type == 'itemc' ||
    test.type == 'itemd') {
    do something.
}
32
idfah
var possibilities = {
  "itema": 1,
  "itemb": 1,
  "itemc": 1,
…};
if (test.type in possibilities) { … }

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

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

または、アイテムがそれほど均一でない場合:

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

優れた答えですが、コードの1つを関数でラップすることで、コードをはるかに読みやすくすることができます。

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

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

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

関数に明示的に名前を付けて、テスト対象がすぐにわかるようにし、コードをスキャンして理解しやすくします。

10
Fran Hoey

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

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

何かのようなもの:

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

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

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
Muhammad Umer

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

Test.typeで発生しないことがわかっている文字を選択し、区切り文字として使用し、それらすべてを1つの長い文字列に貼り付けて、以下を検索します。

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

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

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

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

2
CupawnTae

これを達成する私のお気に入りの方法の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

2
jcreamer898

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
unmultimedio

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

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

それを呼び出します:

…
    if (isTypeDefined(test)) {
…
}
...
2
zaph

この種の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
scunliffe

これは単純な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番目のセクションを使用してループを最終的に終了します。

1
anon