如何在不重复自己的情况下编写三元运算符(又名 if)表达式
Posted
技术标签:
【中文标题】如何在不重复自己的情况下编写三元运算符(又名 if)表达式【英文标题】:How to write a ternary operator (aka if) expression without repeating yourself 【发布时间】:2017-09-07 15:44:11 【问题描述】:例如,像这样的:
var value = someArray.indexOf(3) !== -1 ? someArray.indexOf(3) : 0
有没有更好的写法?同样,我不是在寻求上述确切问题的答案,只是一个示例,说明您何时可能在三元运算符表达式中重复操作数...
【问题讨论】:
所以if
而不是if/else
just an example of when you might have repeated things in the ternary
不要重复计算表达式。这就是变量的用途。
我们如何确定3
不在someArray
的索引0
中?
你的目标是什么?您是要减少行长,还是特别要避免在三元组中重复变量?前者是可能的,后者不是(至少,不使用三元组)。
为什么不改用Math.max(someArray.indexOf(3), 0)
?
【参考方案1】:
对于这种特殊情况,您可以使用逻辑 ||
运算符进行短路。由于0
被认为是虚假的,您可以将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);
【讨论】:
【参考方案2】:代码应该是可读的,所以简洁并不意味着不惜一切代价保持简洁——因为你应该重新发布到https://codegolf.stackexchange.com/——所以我建议使用第二个名为index
的局部变量来最大限度地提高阅读的可理解性(我注意到,运行时成本也最低):
var index = someArray.indexOf( 3 );
var value = index == -1 ? 0 : index;
但如果你真的想减少这种表达,因为你对你的同事或项目合作者是一个残忍的虐待狂,那么你可以使用以下 4 种方法:
1:var
语句中的临时变量
您可以使用var
语句的功能来定义(和分配)第二个临时变量index
(用逗号分隔):
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 );
【讨论】:
所有这些都是那种“聪明”的编码,让我盯着它看了几秒钟,然后想“嗯,我想这行得通”。它们无助于清晰,而且由于阅读的代码比编写的多,因此更重要。 @g.rocket 方法 1 是 100% 可读且清晰的,如果您想避免重复,这是唯一的方法(如果您调用一些复杂且有问题的函数而不是一个简单的indefOf
)
同意,#1 的可读性和可维护性很好,其他两个就没那么多了。
对于#3,我相信你可以简化为var value = ((value = someArray.indexOf(3)) === -1 ? 0 : value);
而不是使用逗号。
在第二个例子中自执行匿名函数你可以省略箭头函数的括号。 var value = ( x => x !== -1 ? x : 0 ) ( arr.indexOf(3) );
因为只有一个参数。【参考方案3】:
编辑:在这里,Nullary-coalescing 的提案现在在 JavaScript 中!
使用||
const result = a ? a : 'fallback value';
等价于
const result = a || 'fallback value';
如果将a
转换为Boolean
返回false
,则result
将被分配'fallback value'
,否则为a
的值。
注意极端情况a === 0
,它转换为false
和result
将(错误地)采用'fallback value'
。使用此类技巧需要您自担风险。
PS。诸如 Swift 之类的语言具有 nil-coalescing 运算符 (??
),其用途类似。例如,在 Swift 中,您可以编写 result = a ?? "fallback value"
,它非常接近 JavaScript 的 const result = a || 'fallback value';
【讨论】:
php (>7.0) 和 C# 也支持空值合并运算符。语法糖,但肯定很可爱。 这仅适用于函数在失败时返回错误值的情况,但indexOf()
不能用于此模式。
正确,但他并没有要求具体的例子>“再次,不寻求上述确切问题的答案,只是一个例子,说明你何时可能在三元中重复了一些事情”
这真的||
在JavaScript中是如何工作的吗?如果我对您的理解正确,那么它的工作方式与许多其他主要语言不同(我主要考虑 C 及其后代 [C++、Java 等])即使这确实是 ||
在 JavaScript 中的工作方式,我也会建议不要使用这样的技巧,因为这些技巧需要维护人员了解该语言的特殊怪癖。虽然这个技巧很酷,但我认为这是不好的做法。
另请注意,问题是将值与-1
进行比较。同样,我不能代表 JavaScript 及其怪癖,但通常 -1
将是一个真值,而不是一个假值,因此您的答案在问题的情况下不起作用,当然也不是一般情况,而是只适用于一个特定(但足够常见)的子案例。【参考方案4】:
三元就像一个 if-else,如果你不需要 else 部分,为什么不只是一个 if 代替..
if ((value = someArray.indexOf(3)) < 0) value = 0;
【讨论】:
【参考方案5】:这是一个简单的解决方案,使用 bitwise NOT 和默认值 -1
,稍后会为零。
index = ~(~array.indexOf(3) || -1);
它基本上适用于双位非,它返回原始值或默认值,应用位非后返回零。
让我们来看看真值表:
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
【讨论】:
【参考方案6】:使用extract variable refactoring:
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;
【讨论】:
什么是“提取变量”?这是一个既定的术语吗?【参考方案7】:我可以通过两种方式查看您的问题:您要么想减少行长,要么特别想避免在三元组中重复变量。第一个是微不足道的(许多其他用户已经发布了示例):
var value = someArray.indexOf(3) !== -1 ? someArray.indexOf(3) : 0;
可以(并且应该,给定函数调用)像这样缩短:
var value = someArray.indexOf(3);
value = value !== -1 ? value : 0;
如果您正在寻找一种更通用的解决方案来防止三元中的变量重复,如下所示:
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
【讨论】:
这个问题被标记为 javascript 并且没有提到 C#。只是想知道为什么以 C# 特定示例结尾。 我错过了问题上的 javascript 标签;删除了 C#。【参考方案8】:我个人更喜欢两种变体:
如果像@slebetman 建议的那样纯属
分离函数,将无效值替换为默认值,如下例所示:
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,选项2尊重问题的内联意图,可以有效地应用于javascript以外的其他语言,并促进模块化。【参考方案9】:我认为||
运算符可以定制为indexOf
:
var value = ((someArray.indexOf(3) + 1) || 1) - 1;
返回值上移 1,从 -1 变为 0,这是错误的,因此被第二个 1 替换。然后它被移回。
但是,请记住,可读性优于避免重复。
【讨论】:
【参考方案10】:使用辅助函数:
function translateValue(value, match, translated)
return value === match ? translated : value;
现在你的代码可读性很强,没有重复。
var value = translateValue(someArray.indexOf(3), -1, 0);
编码关注点的层次结构是:
-
正确(包括真正的性能或 SLA 问题)
清除
简洁
快速
到目前为止,页面上的所有答案似乎都是正确的,但我认为我的版本具有最高的清晰度,这比简洁更重要。如果你不计算辅助函数——因为它可以被重用——它也是最简洁的。不幸的是,使用辅助函数的有点类似的建议使用了一个 lambda,对我来说,它只是掩盖了它在做什么。一个更简单的函数,它的目的是不使用 lambda,只使用值,对我来说要好得多。
附:如果你喜欢 ES6 语法:
const translateValue = (value, match, translated) => value === match ? translated : value;
let value = translateValue(someArray.indexOf(3), -1, 0); // or const
【讨论】:
“我的版本清晰度最高” - 我不同意。函数名太长,命名参数 input 和 output 根本没有帮助。 你能推荐更好的名字吗?我很乐意招待他们。您的投诉完全是关于化妆品的,所以让我们修理化妆品吧。正确的?否则你只是自行车脱落。 在某些情况下,这样的辅助函数会很有用。在这种情况下,它仅替换三元运算符的特定情况,您的功能将不太清楚。我不会记住你的函数做了什么,每次遇到它我都得再去查找它,我永远不会记得使用它。 @Aaron 这是对特定用例的合理评估。值得一提的是,我最初的函数名称是translateValueIfEqual
,我认为它更具描述性,但在有人认为它太长后我更改了它。很像 Access 中的Nz
函数,如果你知道它,你就知道它,如果你不知道,你就不知道。在现代 IDE 中,您只需按一个键即可跳转到定义。而后备将是中间变量。我真的没有看到这里有很大的缺点。
这只是表明,如果您向 5 个不同的工程师提出同一个问题,您将得到 10 个不同的答案。【参考方案11】:
我喜欢@slebetman 的回答。它下面的评论表达了对变量处于“中间状态”的担忧。如果这对您来说是一个大问题,那么我建议将其封装在一个函数中:
function get_value(arr)
var value = arr.indexOf(3);
if (value === -1)
value = 0;
return value;
然后打电话
var value = get_value( someArray );
如果您在其他地方使用它们,您可以做更多通用功能,但如果是非常具体的情况,请不要过度设计。
但老实说,除非我需要在多个地方重复使用,否则我只会像 @slebetman 那样做。
【讨论】:
【参考方案12】:您可能正在寻找合并运算符。幸运的是,我们可以利用 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);
打印 0
,1
,"coalesce"
。
@CharlieHarding 是的,但一般不建议在遍历数组时使用 for-in 运算符。见***.com/a/4374244/1486100【参考方案13】:
不是真的,只是使用另一个变量。
您的示例可以概括为这样的内容。
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)
【讨论】:
【参考方案14】:对于数字
您可以使用Math.max()
函数。
var value = Math.max( someArray.indexOf('y'), 0 );
如果是这种情况,它将保持结果的边界从0
直到第一个结果大于0
。如果indexOf
的结果是-1
,它将返回大于-1
的0。
对于布尔值和布尔值
对于 JS,AFAIK 没有特别的通用规则,因为如何评估 falsy 值。
但如果大多数时候可以帮助您的是 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
是falsy 值。
【讨论】:
谢谢。我想我的例子很糟糕,我只是举一个一般的例子哈哈,而不是寻求确切问题的解决方案。我遇到了一些 scnarios,例如我想使用三元的示例,但最终重复了:( 第一个例子,我们如何确定3
或"y"
不在someArray
的索引0
中?
在Math.max
示例中? indexOf
返回元素的索引,如果未找到该元素返回 -1,因此您有机会从 -1 获取到字符串长度的数字,然后使用 Math.max
您只需将边界设置为 0 到消除返回 -1 机会的长度,
@mitogh OP 代码中的逻辑造成了一个问题,尽管它是通用示例;其中 0 既可以表示数组中匹配元素的索引0
,也可以表示0
设置为Math.max()
;或在 OP 的条件运算符处。考虑var value = Math.max( ["y"].indexOf("y"), 0 )
。您如何确定返回的是哪个0
? 0
传递给Math.max()
调用,或者0
反映数组中"y"
的索引?
@guest271314 好主意,但我认为这是否是一个问题取决于上下文。也许 0 来自哪里并不重要,唯一重要的是它不是 -1。一个例子:也许你需要从一个数组中挑选一个项目。你想要一个特定的项目(在 OP 中,数字 3),但如果它不在数组中,你仍然需要一个项目,并且你可以默认为第一个项目是什么,假设你知道数组不是t 为空。【参考方案15】:
鉴于问题中的示例代码,尚不清楚如何确定3
是否设置在someArray
的索引0
处。从.indexOf()
返回的-1
在这种情况下很有价值,目的是排除可能是匹配的假定不匹配。
如果数组中不包含3
,则返回-1
。我们可以将1
添加到.indexOf()
的结果中,以评估为false
,结果为-1
,然后是||
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);
【讨论】:
【参考方案16】:您可以使用重新分配:
将变量初始化为一个值 使用&&
运算符的序列化进行重新赋值,因为如果第一个条件为假,则不会计算第二个表达式
例如
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);
【讨论】:
@MinusFour 在一定程度上。变量是重复的,不是表达式someArray.indexOf
只执行一次
你重复value
。
@MinusFour 正确,但这对于较大的表达式更有用,与保存操作相比,重复变量是微不足道的。我的猜测是 OP 不适用于 -1
和 0
;否则max()
将是最好的选择
对...但问题是...“如何编写三元组不重复自己”
它还写着Again, not seeking an answer to the exact question above
,这将问题留给了解释;)【参考方案17】:
我个人认为最好的方法仍然是旧的if
声明:
var value = someArray.indexOf(3);
if (value === -1)
value = 0;
【讨论】:
要明确,这个答案提倡使用变量来存储中间结果,而不是使用if
语句代替三元组。三元语句在作为表达式的一部分执行选择时仍然经常有用。
@Triptych:再看一遍。它根本不使用中间变量。相反,它将结果直接分配给最终变量,然后在满足条件时覆盖它。
不正确。第一行之后存储在value
中的值是中间值。它不包含正确的值,然后将其固定在下一行,因此是中间值。只有在if
语句得出结论value
包含正确的值之后。 OP 中的三元组是一个更好的解决方案,因为它永远不会进入中间状态。
@JackAidley:“OP 中的三元组是一个更好的解决方案,因为它永远不会进入中间状态。” - 我将不得不不同意。这比 OP 的代码可读性强得多,而且完全是惯用的。它还使 OP 逻辑中的错误对我来说更加明显(即,如果 indexOf() 返回零会发生什么?如何区分“真实”零和“未找到”零?)。
这和我会做的差不多,只是这里的value
在技术上发生了变异,我尽量避免。以上是关于如何在不重复自己的情况下编写三元运算符(又名 if)表达式的主要内容,如果未能解决你的问题,请参考以下文章