如何在不重复的情况下编写三元运算符(又名if)表达式


104

例如,如下所示:

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

有没有更好的方法可以写呢?再说一次,我不是要寻找上述确切问题的答案,而只是当您可能在三元运算符表达式中重复操作数时的一个示例...


2
因此,这if不是if/else
-zer00ne

53
just an example of when you might have repeated things in the ternary不要重复计算的表达式。这就是变量的用途。
vol7ron

4
我们如何确定3不在索引0的位置someArray
guest271314

3
您的目标是什么?您是要减小行长,还是要专门避免在三元数中重复变量?前者是可能的,后者则不是(至少不使用三元)。
asgallant

5
为什么不使用它Math.max(someArray.indexOf(3), 0)呢?
301_Moved_Permanently

Answers:


176

我个人认为,执行此操作的最佳方法仍然是好的旧if声明:

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

32
明确地说,此答案提倡使用变量存储中间结果,而不是使用if语句代替三进制。三元语句在执行选择作为表达式的一部分时仍然经常有用。
三联画

4
@Triptych:再看一遍。它根本不使用中间变量。相反,它将结果直接分配给最终变量,然后在满足条件的情况下将其覆盖。
slebetman

14
不正确 value第一行之后存储的值是中间值。它不包含正确的值,该值随后固定在下一行,因此是中间值。仅在if语句结束后才value包含正确的值。OP中的三元是一个更好的解决方案,因为它永远不会进入中间状态。
杰克·艾德利

44
@JackAidley:“ OP中的三元组是一个更好的解决方案,因为它永远不会进入中间状态。” -我将不得不不同意。这比OP的代码更具可读性,而且完全是惯用的。这也使OP逻辑中的错误对我来说更加明显(即,如果indexOf()返回零,会发生什么?如何区分“真实”零与“未找到”零?)。
凯文

2
这与我的工作很接近,除了value这里是技术上的变异,我试图避免。
Dave Cousineau

102

代码应易于阅读,因此简洁明了不意味着要花多少钱,否则您应该重新发布到https://codegolf.stackexchange.com/-因此,我建议使用第二个局部变量,以index最大程度地提高阅读的理解度(我还注意到,运行时成本也极低):

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

但是,如果您真的想减少这种表达方式,因为您是同事或项目合作者的残酷虐待狂,那么您可以使用以下4种方法:

1:a中的临时变量 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/zh-CN/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
所有这些都是一种“聪明”的编码,它使我凝视了几秒钟,然后才想到“嗯,我想那是可行的”。它们对提高清晰度没有帮助,并且由于读取的代码多于编写的代码,因此更为重要。
加文S.扬西

18
@ g.rocket方法1是100%可读和清晰的,并且是避免重复的唯一方法(如果您调用某些复杂且有问题的函数而不是简单的函数,那将是非常有害的indefOf
edc65

5
同意#1具有很高的可读性和可维护性,其他两个则不是那么容易。
forgivenson

6
对于#3,我相信您可以简化为var value = ((value = someArray.indexOf(3)) === -1 ? 0 : value);而不是使用逗号。
IllusiveBrian

2
在第二个示例自执行匿名函数中,可以省略箭头函数的括号。var value = ( x => x !== -1 ? x : 0 ) ( arr.indexOf(3) );因为这只是一个参数。
亚历克斯·查尔

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 17-4-12

1
在第一个例子,我们如何确定3"y"不在索引0someArray
guest271314

在这个Math.max例子上?indexOf返回元素的索引,如果未找到该元素,则返回-1,因此您有机会获得一个从-1到字符串长度的数字,然后Math.max只需将边界从0设置为该长度即可消除该机会返回-1,
Crisoforo Gaspar'Apr 12'17

@mitogh OP处的代码逻辑创建了一个问题,尽管是通用示例;其中0都可以表示0数组中匹配元素的索引,或者0设置为Math.max();或在OP的条件运算符处。考虑一下var value = Math.max( ["y"].indexOf("y"), 0 )。您如何确定0退回的是什么?0传递给Math.max()调用,还是0反映"y"数组内的索引?
guest271314

@ guest271314很好的想法,但是我认为这取决于问题是否存在。也许0的来源无关紧要,唯一重要的是它不是-1。一个例子:也许您需要从数组中选择一个项目。您需要一个特定的项目(在OP中,数字3),但是如果该项目不在数组中,那么您仍然需要一个项目,如果您知道数组为'',则可以默认使用第一个项目为准。没空。
凯夫

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中进行空字符合并的建议!


||

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

相当于

const result = a || 'fallback value';

如果强制转换aBooleanreturn falseresult则将分配'fallback value',否则为a


注意边缘情况下a === 0,其造型为falseresult会(错误地)走'fallback value'。使用此类技巧需要您自担风险。


PS。诸如Swift之类的语言具有n -coalescing运算符(??),其作用类似。例如,在Swift中,您将编写result = a ?? "fallback value"与JavaScript非常接近的代码const result = a || 'fallback value';


3
PHP(> 7.0)和C#也支持null合并运算符。语法糖,但肯定很可爱。
Hissvard

5
这仅在函数失败时返回假值时才有效,但indexOf()不能在此模式下使用。
Barmar

1
是的,但他不是要问具体的例子>“再次,不是要寻找上述确切问题的答案,只是一个例子,说明何时您可能在三进制中重复了一些事情”,请注意,他是在寻求一种通用的方法替换重复的三元数(结果= a?a:b)。并且重复三进制等效于|| (结果= a || b)
Lyubomir

2
真的||JavaScript中的工作方式吗?如果我对您的理解正确,那么它的工作方式将与许多其他主要语言不同(我主要考虑的是C及其后代[C ++,Java等])。即使这确实是||JavaScript的工作方式,我还是建议不要使用这样的技巧需要维护人员了解有关该语言的特殊怪癖。虽然这个技巧很酷,但我认为这是不好的做法。
Loduwijk

1
另请注意,问题是将值与进行比较-1。再说一次,我不能代表JavaScript及其怪癖,但通常-1会是一个真实的值,而不是一个错误的值,因此您的回答在问题的情况下将不起作用,当然在一般情况下也不会,而是仅在特定的情况下才有效(但足够普遍)子情况。
Loduwijk

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

我个人更喜欢两个变体:

  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 '17

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

我可以通过两种方式查看您的问题:要么减少行长,要么特别想避免在三进制中重复变量。首先是琐碎的(许多其他用户都张贴了示例):

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为时用于返回RHS falsey

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

1
该问题被标记为javascript,没有提及C#。只是想知道为什么以C#特定示例结尾。
Loduwijk '17

我错过了这个问题上的javascript标签;删除了C#。
asgallant

3

使用辅助功能:

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

现在,您的代码非常易读,并且没有重复。

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

编码关注的层次结构是:

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

到目前为止,页面上的所有答案似乎都是正确的,但是我认为我的版本具有最高的清晰度,这比简洁更为重要。如果您不计算帮助程序功能(可以重用),那么它也是最简洁的。不幸的是,使用辅助函数的建议不幸地使用了一个lambda,对我来说,它只是掩盖了它的功能。对我来说,一个简单的功能只需要一个值,而这个功能就不需要lambda了。

PS如果您喜欢ES6语法:

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

2
“我的版本清晰度最高”-我不同意。函数名称太长,命名参数输入输出根本没有帮助。
adelphus

那你能推荐更好的名字吗?我很乐意招待他们。您的投诉仅与化妆品有关,因此让我们修复化妆品。对?否则,您只是骑自行车而已。
ErikE

在某些情况下,这样的辅助功能可能很有用。在这种情况下,如果仅替换特定情况的三元运算符,您的功能将不太清楚。我不会记住您的函数的功能,每次遇到它都必须再次查找它,而且我永远也不会记得使用它。
Loduwijk

1
@Aaron对于特定用例,这是一个合理的评估。对于它的价值,我translateValueIfEqual认为原来的函数名更具描述性,但是在有人认为它太长之后我更改了它。就像NzAccess中的功能一样,如果您知道它,就知道它,如果不知道,就不知道。在现代IDE中,您只需按一个键即可跳至定义。而后备将是中间变量。我在这里看不到太大的缺点。
ErikE

1
这只是表明,如果您向5位不同的工程师提出相同的问题,您将获得10个不同的答案。
Loduwijk

2

我认为||运营商可以针对indexOf

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

返回的值将上移1,从-1变为0,这是错误的,因此将其替换为第二个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

您可以使用重新分配:

  • 将变量初始化为一个值
  • 使用&&运算符的序列化进行重新分配,因为如果第一个条件为假,则将不计算第二个表达式

例如

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


3
@MinusFour在一定程度上。重复该变量,而不是someArray.indexOf只执行一次表达式
vol7ron 17-4-12

你再说一遍value
MinusFour

3
@MinusFour正确,但这对于较大的表达式更有用,与保存操作相比,重复变量并不重要。我的猜测是OP无法与-1和一起使用0;否则,这max()将是最佳选择
vol7ron

2
是的...但问题是...“如何不重复自己就写三元”
MinusFour

4
它还说Again, not seeking an answer to the exact question above,这使问题
有待

0

给定Question上的示例代码,尚不清楚如何确定3在的索引0处是否设置了someArray。在这种情况下,-1从中返回的值.indexOf()将很有价值,以排除可能是匹配项的假定不匹配项。

如果3未包含在数组中,-1将返回。我们可以添加1到的结果.indexOf()来评价作为false对结果是-1,在随后|| OR运营商和0。当value被引用时,减去1以获得阵列或的元素的索引-1

这导致回简单地使用.indexOf(),并检查-1if状态。或者,valueundefined避免与原始参考文献相关的评估条件的实际结果造成混淆而定义。

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

对于这种特殊情况,您可以使用逻辑||运算符进行短路。由于0被认为falsy,您可以添加1到您的指标,因此,如果index+10,那么你会得到逻辑或为你的结果的右侧,否则,你会得到你index+1。由于所需的结果由偏移1,因此您可以从中减去1以获得索引:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.