如何写一个三元运算符(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 : y
到idx == -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
,将其转换为false
, result
会(不正确)采取'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;
在实践中,使用比index
, condition
和value
更有意义的名称。
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
我个人更喜欢两个变种:
-
纯粹如果像@slebetmanbuild议的那样
-
独立函数,用默认值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);
编码问题的层次是:
- 正确(包括真实的性能或SLA问题)
- 明确
- 简洁
- 快速
到目前为止,页面上的所有答案似乎都是正确的,但是我认为我的版本具有最高的清晰度,这比简洁更重要。 如果你不计算帮助函数 – 因为它可以被重用 – 它也是最简洁的。 不幸的是,使用帮助函数的一些类似的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
运算符和0
。 value
被引用时,减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;