自分自身を繰り返さずに三項演算子(別名if)式を記述する方法


104

たとえば、次のようなもの:

var value = someArray.indexOf(3) !== -1 ? someArray.indexOf(3) : 0

それを書くより良い方法はありますか?繰り返しますが、私は上記の正確な質問に対する答えを求めているのではなく、三項演算子式でオペランドを繰り返した可能性がある場合の例にすぎません...


2
したがって、anではifなくif/else
zer00ne 2017

53
just an example of when you might have repeated things in the ternary計算式を繰り返さないでください。それが変数の目的です。
vol7ron 2017

4
それ3がインデックス0にないことをどのように判断しsomeArrayますか?
guest271314 2017

3
ここであなたの目標は何ですか?行の長さを短くしようとしていますか、それとも、3項での変数の繰り返しを避けようとしていますか?前者は可能ですが、後者は不可能です(少なくとも、3元を使用しません)。
素晴らしかった

5
Math.max(someArray.indexOf(3), 0)代わりに使用しないのはなぜですか?
301_Moved_Permanently

回答:


176

個人的に私はこれを行う最善の方法がまだ古き良きif声明であることを発見しました:

var value = someArray.indexOf(3);
if (value === -1) {
  value = 0;
}

32
明確にするために、この回答ifは、3項の代わりにステートメントを使用するのではなく、変数を使用して中間結果を格納することを推奨しています。三項ステートメントは、式の一部として選択を実行するために依然として頻繁に役立ちます。
トリプティク2017

4
@Triptych:もう一度見てください。中間変数は一切使用しません。代わりに、結果を最終的な変数に直接割り当て、条件が満たされた場合は上書きします。
slebetman 2017

14
不正解です。value最初の行の後に格納されている値は中間です。次の行で修正される正しい値が含まれていないため、中間値になります。正しい値ifvalue含まれているのは、ステートメントが終了した後だけです。OPの三項は、中間状態に入ることがないため、これよりも優れたソリューションです。
Jack Aidley

44
@JackAidley:「OPの三項は、中間状態に入ることがないため、これよりも優れたソリューションです。」-私は同意しなければならないでしょう。これはOPのコードよりもはるかに読みやすく、完全に慣用的です。また、OPのロジックのバグが少しわかりやすくなります(つまり、indexOf()がゼロを返すとどうなりますか?「実際の」ゼロと「見つからない」ゼロをどのように区別しますか?)。
ケビン

2
これは私がやろうとしていることとほぼ同じですが、valueここでは技術的に変更されているため、避けようとしています。
Dave Cousineau 2017

102

コードは、読取り可能である必要がありますので、という簡潔べきコストは何でもない平均ビーイングの簡潔な-あなたに再投稿する必要があり、そのためにhttps://codegolf.stackexchange.com/ -その代わりに、私は名前の第二のローカル変数を使用することをお勧めしますindex(読みわかりやすさを最大化するために、実行時のコストも最小限に抑えられます。

var index = someArray.indexOf( 3 );
var value = index == -1 ? 0 : index;

しかし、同僚やプロジェクトの協力者にとって残酷なサディストであるため、この表現を削減したい場合は、次の4つの方法を使用できます。

1:varステートメント内の一時変数

varステートメントの機能を使用して、indexコンマで区切られたときに2番目の一時変数を定義(および割り当て)できます。

var index = someArray.indexOf(3), value = index !== -1 ? index: 0;

2:自己実行匿名関数

別のオプションは、自己実行の無名関数です。

// Traditional syntax:
var value = function( x ) { return x !== -1 ? x : 0 }( someArray.indexOf(3) );

// ES6 syntax:
var value = ( x => x !== -1 ? x : 0 )( someArray.indexOf(3) );

3:カンマ演算子

JavaScriptがサポートする悪名高い「カンマ演算子」もあり、これはCおよびC ++にも存在します。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator

単一の式を必要とする場所に複数の式を含める場合は、カンマ演算子を使用できます。

これを使用して副作用を導入できます。この場合は、に再割り当てしvalueます。

var value = ( value = someArray.indexOf(3), value !== -1 ? value : 0 );

これが機能するのvar valueは、最初に解釈されるため(ステートメントであるため)、次に左端、最も内側のvalue代入、次にコンマ演算子の右側、次に三項演算子-すべての正当なJavaScriptです。

4:部分式で再割り当て

解説者@IllusiveBrianは、への割り当てがvalue括弧で囲まれた部分式として使用されている場合、コンマ演算子(前の例)の使用は不要であることを指摘しました。

var value = ( ( value = someArray.indexOf(3) ) !== -1 ? value : 0 );

論理式での否定の使用は、人間にとって理解しにくい場合があることに注意してください。上記の例はすべて、次のように変更idx !== -1 ? x : yすることで読みやすくすることができますidx == -1 ? y : x

var value = ( ( value = someArray.indexOf(3) ) == -1 ? 0 : value );

97
これらはすべて、「うん、うまくいくと思う」と思う前に数秒間凝視するような「賢い」コーディングです。それらは明快さの助けにはなりません、そしてコードは書かれている以上に読まれているのでより重要です。
Gavin S. Yancey 2017

18
@ g.rocketアプローチ1は100%読み取り可能で明確であり、繰り返しを避けたい場合に実行する唯一の方法です(単純ではなく複雑で問題のある関数を呼び出す場合は非常に危険ですindefOf
edc65

5
合意されたとおり、#1は非常に読みやすく、保守可能ですが、他の2つはそれほどではありません。
forgivenson

6
#3についてvar value = ((value = someArray.indexOf(3)) === -1 ? 0 : value);は、カンマを使用する代わりに、に簡略化できると思います。
IllusiveBrian

2
2番目の例の自己実行匿名関数では、矢印関数から括弧を省略できます。var value = ( x => x !== -1 ? x : 0 ) ( arr.indexOf(3) );はパラメータが1つしかないためです。
Alex Char

54

数字について

Math.max()関数を使用できます。

var value = Math.max( someArray.indexOf('y'), 0 );

その場合0、最初の結果までの結果の境界をより大きく保ち0ます。以下とからもし結果がindexOfある-1以上であるとして、それは0を返します-1

ブール値とブール値-yの値

JSの場合、偽の値がどのように評価されるのかについては、特にAFAIKの一般的なルールはありません。

しかし、ほとんどの場合、何かがor演算子(||)で役立つ場合:

// Instead of
var variable = this_one === true ? this_one : or_this_one;
// you can use
var variable = this_one || or_this_one;

最初の例でindexOfはが返される可能性が0あり、評価0 || -1すると偽の値が返される-1ため、これには非常に注意する必要0があります。


2
ありがとう。私の例は悪いと思います。正確な質問の解決策を求めていない一般的な例を挙げているだけです。私は三項を使用したい例のようないくつかのスナリオに直面しましたが、結局は繰り返します:(
user1354934

1
最初の例では、どのように3それ"y"がインデックス0にあるsomeArrayかどうかを判断するのですか?
guest271314 2017

上のMath.max例?indexOf要素のインデックスを返し、要素が見つからない場合は-1を返すので、-1から文字列の長さまでの数を取得できる可能性があります。次にMath.max、0から長さまでの境界を設定して、可能性を排除します-1を返すには
Crisoforo Gaspar 2017

@mitogh OPのコードのロジックは、問題ですが、一般的な例です。0は両方とも0、配列内の一致する要素のインデックスを示すか、またはに0設定されMath.max()ます。またはOPの条件付き演算子で。考えてくださいvar value = Math.max( ["y"].indexOf("y"), 0 )。どちら0が返却されているかをどのように判断しますか?呼び出しに0渡されたか、配列内のインデックスを反映していますか?Math.max()0"y"
guest271314 2017

@ guest271314良い考えですが、それが問題であるかどうかは状況に依存すると思います。おそらく、0がどこから来たかは問題ではなく、唯一の重要なことは、それが-1ではないということです。例:配列から項目を選択する必要があるかもしれません。特定のアイテム(OP内の数値3)が必要ですが、それが配列にない場合でもアイテムが必要です。配列がわかっていると仮定して、最初のアイテムが何であってもデフォルトで問題ありません。空です。
Kev

27

実際には、別の変数を使用してください。

あなたの例はこのようなものに一般化されます。

var x = predicate(f()) ? f() : default;

計算された値をテストし、それが何らかの述語を渡す場合、その値を変数に割り当てます。計算値の再計算を回避する方法は明らかです。変数を使用して結果を保存します。

var computed = f();
var x = predicate(computed) ? computed : default;

私はあなたが何を意味するかを理解します-これを行うには少しすっきりした方法があるはずです。しかし、これは(慣用的に)これを行うための最良の方法だと思います。何らかの理由でコードでこのパターンを何度も繰り返していた場合は、小さなヘルパー関数を作成できます。

var setif = (value, predicate, default) => predicate(value) ? value : default;
var x = setif(someArray.indexOf(3), x => x !== -1, 0)

17

編集:これがJavaScript でのNullary-coalescingの提案です!


使用する ||

const result = a ? a : 'fallback value';

に相当

const result = a || 'fallback value';

キャストした場合aBoolean戻りfalseresult割り当てられます'fallback value'、そうでない場合の値をa


a === 0キャストしfalseresult(誤って)取得するエッジケースに注意してください'fallback value'。このようなトリックは自己責任で使用してください。


PS。Swiftなどの言語には、類似の目的を果たすnil-coalescing演算子(??)があります。たとえば、Swiftでは、result = a ?? "fallback value"JavaScriptにかなり近いものを記述します。const result = a || 'fallback value';


3
PHP(> 7.0)およびC#も、null結合演算子をサポートしています。構文上の砂糖ですが、きっと素敵です。
ヒスバード2017

5
これは、関数が失敗したときにfalse値を返す場合にのみ機能しますが、indexOf()このパターンでは使用できません。
Barmar 2017

1
正解ですが、彼は具体的な例を求めていません>「繰り返しますが、上記の正確な質問への回答を求めているのではなく、3項で繰り返した可能性のある例にすぎません」<一般的な方法を求めていることに注意してください反復三項を置き換えます(結果= a?a:b)。そして、三項を繰り返すことは||と同等です (結果= a || b)
リュボミール2017

2
つまり、実際にどのように||JavaScriptで動作しますか?私があなたを正しく理解している場合、他の多くの主要言語とは異なる動作をします(私は主にCおよびその子孫[C ++、Javaなど]と考えています)。それが||JavaScriptで実際に機能する方法であるとしても、使用しないようにアドバイスしますこのようなトリックは、メンテナーが言語についての特別な癖を知っている必要があります。そのトリックはクールですが、私はそれを悪い習慣だと考えます。
Loduwijk

1
また、質問が値をと比較していることにも注意してください-1。繰り返しますが、JavaScriptとその癖について話すことはできませんが、一般的に-1は真の値であり、偽の値ではありません。そのため、あなたの答えは質問のケースでは機能せず、一般的なケースでは機能せず、特定の(しかし、十分に一般的です)サブケース。
Loduwijk 2017

8

抽出変数リファクタリングを使用します

var index = someArray.indexOf(3);
var value = index !== -1 ? index : 0

const代わりにを使用することをお勧めしますvar。追加の抽出を行うこともできます。

const index = someArray.indexOf(3);
const condition = index !== -1;
const value = condition ? index : 0;

実際には、より意味のある名前を使用してindexconditionvalue

const threesIndex = someArray.indexOf(3);
const threeFound = threesIndex !== -1;
const threesIndexOrZero = threeFound ? threesIndex : 0;

「抽出変数」とは何ですか?それは確立された用語ですか?
Peter Mortensen

6

あなたはおそらく合体演算子を探しています。幸い、Arrayプロトタイプを利用して作成できます。

Array.prototype.coalesce = function() {
    for (var i = 0; i < this.length; i++) {
        if (this[i] != false && this[i] != null) return this[i];
    }
}

[null, false, 0, 5, 'test'].coalesce(); // returns 5

これは、関数にパラメーターを追加することにより、ケースにさらに一般化できます。

Array.prototype.coalesce = function(valid) {
    if (typeof valid !== 'function') {
        valid = function(a) {
            return a != false && a != null;
        }
    }

    for (var i = 0; i < this.length; i++) {
        if (valid(this[i])) return this[i];
    }
}

[null, false, 0, 5, 'test'].coalesce(); // still returns 5
[null, false, 0, 5, 'test'].coalesce(function(a){return a !== -1}); // returns null
[null, false, 0, 5, 'test'].coalesce(function(a){return a != null}); //returns false

新しい要素がすべての配列内のインデックスになるため、配列のプロトタイプに追加するのは危険です。指数を反復する新しい方法を含み、この手段はfor (let x in ['b','c']) console.log(x);印刷01"coalesce"
チャーリーハーディング

@CharlieHarding Trueですが、配列をループするときにfor-in演算子を使用することは通常お勧めできません。stackoverflow.com/a/4374244/1486100を
Tyzoid

6

私は個人的に2つのバリアントを好みます。

  1. 純粋な場合、@ slebetmanのように

  2. この例のように、無効な値をデフォルトの値に置き換える別の関数:

function maskNegative(v, def) {
  return v >= 0 ? v : def;
}

Array.prototype.indexOfOrDefault = function(v, def) {
  return maskNegative(this.indexOf(v), def);
}

var someArray = [1, 2];
console.log(someArray.indexOfOrDefault(2, 0)); // index is 1
console.log(someArray.indexOfOrDefault(3, 0)); // default 0 returned
console.log(someArray.indexOfOrDefault(3, 123)); // default 123 returned


1
+1、オプション2は質問のインラインインテントを尊重し、JavaScript以外の言語に効率的に適用でき、モジュール性を促進します。
Devsman 2017

5

@slebetmanの答えが好きです。その下のコメントは、変数が「中間状態」にあることに懸念を表明しています。これがあなたにとって大きな懸念であるなら、私はそれを関数にカプセル化することを勧めます:

function get_value(arr) {
   var value = arr.indexOf(3);
   if (value === -1) {
     value = 0;
   }
   return value;
}

次に電話する

var value = get_value( someArray );

他の場所で使用している場合は、より一般的な関数を実行できますが、非常に特殊なケースである場合は、過度に設計しないでください。

しかし、正直に言うと、いくつかの場所から再利用する必要がない限り、@ slebetmanとして実行します。


4

あなたの質問を見るために私が見ることができる2つの方法があります:あなたは行の長さを短くしたいか、または特に三項で変数を繰り返さないようにするかです。最初は簡単です(そして他の多くのユーザーが例を投稿しています):

var value = someArray.indexOf(3) !== -1 ? someArray.indexOf(3) : 0;

次のように短縮することができます(関数呼び出しが与えられれば、そうする必要があります)。

var value = someArray.indexOf(3);
value = value !== -1 ? value : 0;

次のように、3項での変数の繰り返しを防ぐより一般的なソリューションを探している場合:

var value = conditionalTest(foo) ? foo : bar;

foo一度だけ現れるところ。次の形式の解決策を破棄する:

var cad = foo;
var value = conditionalTest(foo) ? cad : bar;

技術的には正しいが要点が欠けている場合は、運が悪いです。求める構文が簡潔な演算子、関数、およびメソッドはありますが、そのような構造は、定義上、三項演算子ではありません。

例:

JavaScript、||LHSがfalsey次の場合にRHSを返すために使用:

var value = foo || bar; // equivalent to !foo ? bar : foo

1
質問はjavascriptでタグ付けされており、C#については言及されていません。なぜC#固有の例で終わるのか疑問に思います。
Loduwijk 2017

質問のjavascriptタグを見逃しました。C#を削除しました。
素晴らしく、

3

ヘルパー関数を使用します。

function translateValue(value, match, translated) {
   return value === match ? translated : value;
}

これでコードは非常に読みやすくなり、繰り返しはありません。

var value = translateValue(someArray.indexOf(3), -1, 0);

コーディングの問題の階層は次のとおりです。

  1. 正解(真のパフォーマンスまたはSLAの懸念を含む)
  2. 晴れ
  3. 簡潔な
  4. 速い

これまでのページの答えはすべて正しいように見えますが、私のバージョンは、簡潔さよりも重要である最高の明快さを持っていると思います。ヘルパー関数は、再利用できるので数えない場合も、最も簡潔です。ヘルパー関数を使用するというやや類似した提案は、残念ながらラムダを使用しています。ラムダは、私には、それが何をしているかを覆い隠しています。ラムダをとらず、単なる値をとる1つの目的を持つ単純な関数のほうがはるかに優れています。

PS ES6の構文が気に入った場合:

const translateValue = (value, match, translated) => value === match ? translated : value;
let value = translateValue(someArray.indexOf(3), -1, 0); // or const

2
「私のバージョンは最高の明快さを持っています」-私は同意しません。関数名は、あまりにも長いですし、パラメータの命名入力出力はすべての親切ではありません。
adelphus 2017

それで、もっと良い名前を提案できますか?私はそれらを楽しませていただければ幸いです。あなたの不満は化粧品に関するものだけなので、化粧品を修正しましょう。正しい?それ以外の場合は、自転車の脱落だけです。
ErikE 2017

場合によっては、このようなヘルパー関数が役立つことがあります。この場合、それが三項演算子の特定のケースのみを置き換える場合、関数は不明確になります。私はあなたの関数が何をしているのか覚えておくつもりはありません。それに出くわすたびにもう一度調べなければならず、それを使うことを決して覚えません。
Loduwijk 2017

1
@Aaronこれは、特定のユースケースに対する妥当な評価です。元の関数名はわかりtranslateValueIfEqualやすいと思いましたが、誰かが長すぎると思って変更しました。NzAccess の関数と同じように、知っていればわかるし、わかっていなければ知りません。最近のIDEでは、キーを押すだけで定義にジャンプできます。そして、フォールバックは中間変数になります。ここには大きな欠点はありません。
ErikE 2017

1
これは、5人の異なるエンジニアに同じ質問をした場合、10個の異なる回答が得られることを示しています。
Loduwijk

2

私が思うに||、オペレータがに合わせて調整することができますindexOf

var value = ((someArray.indexOf(3) + 1) || 1) - 1;

戻り値は1だけ上にシフトされ、-1から0になります。これは誤っているため、2番目の1に置き換えられます。次に、後ろにシフトされます。

ただし、繰り返しを避けるよりも読みやすさが優れていることに注意してください。


2

これはビットごとのNOTと、-1後でゼロになるデフォルト値を持つ単純なソリューションです。

index = ~(~array.indexOf(3) || -1);

これは基本的に、元の値またはデフォルト値を返すダブルビットワイズNOTで機能し、ビットワイズNOTの適用後にゼロを返します。

真実の表を見てみましょう:

 indexOf    ~indexOf   boolean    default     value      result         comment
---------  ---------  ---------  ---------  ---------  ---------  ------------------
      -1          0     falsy          -1         -1          0   take default value
       0         -1    truthy                     -1          0
       1         -2    truthy                     -2          1
       2         -3    truthy                     -3          2

0

再割り当てを使用できます:

  • 変数を1つの値に初期化する
  • &&最初の条件が偽の場合、2番目の式は評価されないため、再割り当てに演算子のシリアル化を使用します

var value = someArray.indexOf(3);
value == -1 && (value=0);


3
@MinusFourある程度。変数は繰り返され、式someArray.indexOfは1回だけ実行されません
vol7ron

あなたは繰り返しvalueます。
MinusFour 2017

3
@MinusFour正解ですが、これは大きな式の場合により便利です。変数を繰り返すことは、演算を保存することと比較して重要ではありません。私の推測では、OPは-1およびで動作していません0。それ以外の場合はmax()、最良のオプションです
vol7ron

2
正解です...しかし問題は...「自分自身を繰り返さずに3値を書く方法」
MinusFour

4
またAgain, not seeking an answer to the exact question above、それは質問を解釈に残します;)
vol7ron

0

Questionのサンプルコードを考えると、の3インデックス0に設定されているかどうかがどのように判断されるかは明確ではありませんsomeArray。この例では、一致する可能性のある推定非一致を除外するために、-1から返された値.indexOf()が役立ちます。

3が配列に含まれていない場合は-1が返されます。私たちは、追加することができます1の結果に.indexOf()として評価するfalse結果であることのため-1に続いて、|| ORオペレータと0valueが参照されている場合、減算1して配列またはの要素のインデックスを取得します-1

これは、ある条件で単に使用.indexOf()して確認することにつながります。または、元の参照に関連する評価された条件の実際の結果に関して混乱の可能性を回避するように定義します。-1ifvalueundefined

var someArray = [1,2,3];
var value = someArray.indexOf(3) + 1 || 1;
console.log(value -= 1);

var someArray = [1,2,3];
var value = someArray.indexOf(4) + 1 || 1;
// how do we know that `4` is not at index `0`?
console.log(value -= 1);

var someArray = [1,2,3];
var value = someArray.indexOf(4) + 1 || void 0;
// we know for certain that `4` is not found in `someArray`
console.log(value, value = value || 0);


0

三項はif-elseのようなものです。else部分が必要ない場合は、代わりに単一のifを使用しないでください。

if ((value = someArray.indexOf(3)) < 0) value = 0;

0

この特定のケースでは、論理||演算子で短絡を使用できます。などが0falsy考えられている、あなたが追加することができ1、あなたのインデックスにあれば、このように、index+1ある0あなたは論理またはあなたの結果としての右側を得るでしょう、そうでない場合、あなたはあなたを得るでしょうindex+1。必要な結果はでオフセットされる1ので、それから減算1してインデックスを取得できます。

const someArray = [1, 2, 3, 4];
const v = ((someArray.indexOf(3)+1) || 1)-1;
console.log(v);

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