如何写一个三元运算符(aka if)expression式而不重复自己

例如,像这样的东西:

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

有没有更好的方式来写这个? 再次,我不是在寻求上述确切的问题的答案,只是一个例子,当你可能在三元运算符expression式中重复操作数…

就我个人而言,我发现做这件事的最好方法仍然是陈旧的陈述:

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

代码应该是可读的,所以简洁不应该意味着简单的费用 – 因为你应该转贴https://codegolf.stackexchange.com/ – 所以相反,我会build议使用第二个局部variables名为index最大化阅读理解(我注意到,运行成本最低):

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

但如果你真的想削减这个expression,因为你是同事或项目合作者的残酷的虐待者,那么这里有4种方法可以使用:

1: var语句中的临时variables

您可以使用var语句的能力来定义(和分配)第二个临时variables:

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

2:自行执行匿名function

另一种select是一个自我执行的匿名函数:

 // 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

如果要在需要单个expression式的位置包含多个expression式,则可以使用逗号运算符。

您可以使用它来引入副作用,在这种情况下,通过重新value

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

这是有效的,因为var value首先被解释(因为它是一个语句), 然后是最左边的,最内部的value赋值,然后是逗号运算符的右边,然后是三元运算符 – 所有合法的JavaScript。

4:重新分配一个子expression式

评论员@IllusiveBrian指出,如果对value的赋值用作括号内的子expression式,则不需要使用逗号运算符(在前面的示例中):

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

请注意,在逻辑expression式中使用负数可能会让人难以遵循 – 所以上述所有示例都可以通过更改idx !== -1 ? x : y来简化读取操作idx !== -1 ? x : y idx !== -1 ? x : yidx == -1 ? y : x idx == -1 ? y : x

数字

您可以使用Math.max()函数。

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

它将保持结果的边界从0到第一个结果大于0如果是这种情况。 如果indexOf的结果是-1 ,它将返回0,因为它大于-1

对于布尔值和布尔值

对于JS而言,AFAIK并没有一个通用的规则,因为如何评估falsy值。

但是,如果有什么可以帮助你,大部分时间是或运营商( || ):

 // 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 0 || -1它将返回-1因为0是一个虚假值。

不是,只是使用另一个variables。

你的例子推广到这样的东西。

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

您正在testing一个计算值,然后将该值赋给一个variables(如果它传递了某个谓词)。 避免重新计算计算值的方法显而易见:使用variables来存储结果。

 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) 

编辑:在这里,现在在JavaScript中Nullary-coalescing的build议!


使用||

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

相当于

const result = a || 'fallback value';

如果将a Boolean转换为Boolean返回false ,则result将被分配'fallback value' ,否则将返回a值。


请注意边缘情况a === 0 ,将其转换为falseresult会(不正确)采取'fallback value' 使用这样的技巧需要您自担风险。


PS。 像Swift这样的语言有nil-coalescing运算符( ?? ),它也有类似的用途。 例如,在Swift中你会写result = a ?? "fallback value" result = a ?? "fallback value" ,它非常接近JavaScript的const result = a || 'fallback value'; const result = a || 'fallback value';

使用抽取variables重构 :

 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; 

您可能正在寻找合并运营商。 幸运的是,我们可以利用Array原型创build一个:

 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 

我个人更喜欢两个变种:

  1. 纯粹如果像@slebetmanbuild议的那样

  2. 独立函数,用默认值replace无效值,如下例所示:

 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 

我喜欢@ slebetman的答案。 其中的评论expression了对variables处于“中间状态”的担忧。 如果这是你的一个大问题,那么我build议把它封装在一个函数中:

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

然后就打电话

 var value = get_value( someArray ); 

如果您在其他地方使用它们,则可以使用更多的通用函数,但如果是非常特殊的情况,则不要过度工程。

但说实话,我只想做@slebetman,除非我需要从几个地方重新使用。

有两种方法可以看到你的问题:你要么减less行长,要么你特别想避免在三元组中重复一个variables。 第一个是微不足道的(许多其他用户已经发布了示例):

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

可以(应该是,给定函数调用)缩短如下:

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

如果你正在寻找一个更通用的解决scheme,防止在三元中重复一个variables,就像这样:

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

foo只出现一次。 舍弃表格的解决scheme:

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

作为技术上正确的,但错过了这一点,那么你是运气不好。 有一些运算符,函数和方法拥有你所寻求的简洁语法,但是这样的结构,根据定义,并不是三元运算符

例子:

javascript,使用|| 当LHS是falsey时候返回RHS:

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

使用帮手function:

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

现在你的代码是非常可读的,没有重复。

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

编码问题的层次是:

  1. 正确(包括真实的性能或SLA问题)
  2. 明确
  3. 简洁
  4. 快速

到目前为止,页面上的所有答案似乎都是正确的,但是我认为我的版本具有最高的清晰度,这比简洁更重要。 如果你不计算帮助函数 – 因为它可以被重用 – 它也是最简洁的。 不幸的是,使用帮助函数的一些类似的build议使用了一个lambda,对我来说,它只是掩盖了它正在做的事情。 一个简单的函数,其目的不是lambda,只是值,对我来说更好。

PS如果你喜欢ES6的语法:

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

我认为|| 操作符可以定制为indexOf

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

返回的值被上移1,从-1开始0,这是假的,因此被第二个1replace。然后它被移回。

但是,请记住,可读性优于避免重复。

这是一个简单的解决scheme, 按位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 

你可以使用重新分配:

  • 初始化variables为一个值
  • 使用&&操作符的序列化进行重新分配,因为如果第一个条件为false,则不会评估第二个expression式

防爆。

 var value = someArray.indexOf(3); value == -1 && (value=0); 
 var someArray = [4,3,2,1]; var value = someArray.indexOf(1); value == -1 && (value=0); console.log('Found:',value); var value = someArray.indexOf(5); value == -1 && (value=0); console.log('Not Found:',value); 

鉴于问题中的示例代码,不清楚如何确定3是否被设置在someArray索引0 someArray 。 从.indexOf()返回的-1在这种情况下是有价值的,目的是排除可能匹配的假定不匹配。

如果数组3中不包含-1则返回-1 。 我们可以给.indexOf()结果加1 ,结果为-1 ,其后跟|| ,对结果计算为false OR运算符和0value被引用时,减1以获得数组元素的索引或-1

这导致简单地使用.indexOf()并在if条件下检查-1 。 或者,将value定义为undefined以避免可能与关于原始参考的评估条件的实际结果混淆。

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

一个三元组就像一个if-else,如果你不需要其他部分,为什么不只是一个单一的,而不是..

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