替代 JS 中的嵌套三元运算符
Alternative to nested ternary operator in JS
我个人喜欢三元运算符,依我的拙见,它们使复杂的表达式变得非常容易理解。拿这个:
const word = (distance === 0) ? 'a'
: (distance === 1 && diff > 3) ? 'b'
: (distance === 2 && diff > 5 && key.length > 5) ? 'c'
: 'd';
然而在我们项目的 ESLINT 规则中禁止嵌套的三元运算符,所以我必须摆脱上面的。
我正在尝试找出这种方法的替代方法。我真的不想把它变成一个巨大的 if / else 语句,但不知道是否还有其他选择。
您的备选方案基本上是:
- 你不想做的
if
/else
- 一个
switch
结合if
/else
我试图想出一个合理的查找地图选项,但很快就变得不合理了。
我会选择 #1,它没那么大:
if (res.distance == 0) {
word = 'a';
} else if (res.distance == 1 && res.difference > 3) {
word = 'b';
} else if (res.distance == 2 && res.difference > 5 && String(res.key).length > 5) {
word = 'c';
} else {
word = 'd';
}
如果所有的大括号和垂直大小都让您感到困扰,没有它们它几乎和条件运算符版本一样简洁:
if (res.distance == 0) word = 'a';
else if (res.distance == 1 && res.difference > 3) word = 'b';
else if (res.distance == 2 && res.difference > 5 && String(res.key).length > 5) word = 'c';
else word = 'd';
(我不提倡那样做,我从不提倡去掉大括号或将 if
后面的语句放在同一行,但其他人有不同的风格观点。)
#2 在我看来更笨重,但这可能更像是一种风格评论:
word = 'd';
switch (res.distance) {
case 0:
word = 'a';
break;
case 1:
if (res.difference > 3) {
word = 'b';
}
break;
case 2:
if (res.difference > 5 && String(res.key).length > 5) {
word = 'c';
}
break;
}
最后,我 不 提倡这一点,您可以利用 JavaScript 的 switch
在 B-syntax language family: case
语句可以是expressions,并与源代码顺序中的switch值匹配:
switch (true) {
case res.distance == 0:
word = 'a';
break;
case res.distance == 1 && res.difference > 3:
word = 'b';
break;
case res.distance == 2 && res.difference > 5 && String(res.key).length > 5:
word = 'c';
break;
default:
word = 'd';
break;
}
有多丑? :-)
如果你所有的真实条件都计算为真实值(所以如果强制为布尔值,问号和分号之间的值计算为真......)你可以使你的三元表达式 return false
作为虚假的表达。然后您可以使用按位或 (||
) 运算符将它们链接起来以测试下一个条件,直到您 return 默认值的最后一个条件。
在下面的示例中,"condsXXX" 数组表示评估条件的结果。 "conds3rd" 模拟第三个条件为真,"condsNone" 模拟没有条件为真。在实际代码中,赋值表达式中有条件 "inlined":
var conds3rd = [false, false, true];
var condsNone = [false, false, false];
var val3rd = (conds3rd[0] ? 1 : false) ||
(conds3rd[1] ? 2 : false) ||
(conds3rd[2] ? 3 : 4);
var valNone = (condsNone[0] ? 1 : false) ||
(condsNone[1] ? 2 : false) ||
(condsNone[2] ? 3 : 4);
alert(val3rd);
alert(valNone);
您的示例最终可能如下所示:
word = ((res.distance === 0) ? 'a' : false) ||
((res.distance === 1 && res.difference > 3) ? 'b' : false) ||
((res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : 'd';
附带说明一下,我不觉得这是一个好看的代码,但它非常接近于像您渴望的那样使用纯三元运算符...
根据我的口味,精心构造的嵌套三元组胜过所有那些混乱的 if 和开关:
const isFoo = res.distance === 0;
const isBar = res.distance === 1 && res.difference > 3;
const isBaz = res.distance === 2 && res.difference > 5 && String(res.key).length > 5;
const word =
isFoo ? 'a' :
isBar ? 'b' :
isBaz ? 'c' :
'd' ;
我最近才遇到这个问题,google 搜索将我带到了这里,我想分享一些我最近发现的关于这个的东西:
a && b || c
和
几乎一样
a ? b : c
只要b
是真实的。如果 b
不真实,您可以使用
解决它
!a && c || b
如果 c
是真实的。
第一个表达式被评估为 (a && b) || c
,因为 &&
比 ||
具有更高的优先级。
如果 a
为真,则 a && b
的计算结果为 b
如果 b
为真,则表达式变为 b || c
,其计算结果为 b
如果它是真实的,就像 a ? b : c
如果 a
是真实的,如果 a
不是真实的那么表达式将根据需要评估为 c
。
在语句层中的 &&
和 ||
技巧以及 ?
和 ||
之间交替使用无嵌套三元 eslint 规则,即非常整洁(尽管我不建议这样做,除非没有其他出路)。
快速演示:
true ? false ? true : true ? false : true ? true ? true : false : true : true
// which is interpreted as
true ? (false ? true : (true ? false : (true ? (true ? true : false) : true))) : true
// now with the trick in alternate levels
true ? (false && true || (true ? false : (true && (true ? true : false) || true))) : true
// all of these evaluate to false btw
实际上我选择了一个 b
总是真实的例子,但如果你只是设置字符串那么这应该可以正常工作,因为甚至 '0'
具有讽刺意味的是真实的。
如果您希望将 const 与嵌套的三元表达式一起使用,您可以将三元表达式替换为函数表达式。
const res = { distance: 1, difference: 5 };
const branch = (condition, ifTrue, ifFalse) => condition?ifTrue:ifFalse;
const word = branch(
res.distance === 0, // if
'a', // then
branch( // else
res.distance === 1 && res.difference > 3, // if
'b', // then
branch( // else
res.distance === 2 && res.difference > 5, // if
'c', // then
'd' // else
)
)
);
console.log(word);
或通过解构使用命名参数...
const branch2 = function(branch) {
return branch.if ? branch.then : branch.else;
}
const fizzbuzz = function(num) {
return branch2({
if: num % 3 === 0 && num % 5 === 0,
then: 'fizzbuzz',
else: branch2({
if: num % 3 === 0,
then: 'fizz',
else: branch2({
if: num % 5 === 0,
then: 'buzz',
else: num
})
})
});
}
console.log(
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16].map(
cv => fizzbuzz(cv)
)
);
编辑
在 python if 表达式之后对其进行建模可能会更清楚:
const res = { distance: 1, difference: 5 };
const maybe = def => ({
if: expr => {
if (expr) {
return { else: () => def };
} else {
return { else: els => els };
}
}
});
const word = maybe('a').if(res.distance === 0).else(
maybe('b').if(res.distance === 1 && res.difference > 3).else(
maybe('c').if(res.distance === 2 && res.difference > 5).else('d')
)
);
console.log(word);
编辑
另一个删除嵌套 if/else 分支的编辑:
const res = { distance: 1, difference: 5 };
const makeResolvedValue = def => {
const elseProp = () => def;
return function value() {
return {
if: () => ({ else: elseProp, value })
};
}
};
const value = def => ({
if: expr => {
if (expr) {
return { else: () => def, value: makeResolvedValue(def) };
} else {
return { else: els => els, value };
}
}
});
// with branching if needed
const word = value('a').if(res.distance === 0)
.else(value('b').if(res.distance === 1 && res.difference > 3)
.else(value('c').if(res.distance === 2 && res.difference > 5)
.else('d')
)
);
console.log(word)
// implicit else option for clarity
const word2 = value('a').if(res.distance === 0)
.value('b').if(res.distance === 1 && res.difference > 3)
.value('c').if(res.distance === 2 && res.difference > 5)
.else('d');
console.log(word2);
word = (res.distance === 0) ? 'a'
: (res.distance === 1 && res.difference > 3) ? 'b'
: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
: 'd';
这是一个较旧的问题,但我会这样做...我将从默认情况开始,然后根据需要更改变量或不加改变地传递它。
var word = 'd';
word = (res.distance === 0) ? 'a' : word;
word = (res.distance === 1 && res.difference > 3) ? 'b' : word
word = (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : word;
我一直在为这些情况使用 switch(true) 语句。在我看来,这种语法感觉比嵌套的 if/else operators
稍微优雅一些
switch (true) {
case condition === true :
//do it
break;
case otherCondition === true && soOn < 100 :
// do that
break;
}
您可以写一个 immediately invoked function expression 使其更具可读性:
const word = (() => {
if (res.distance === 0) return 'a';
if (res.distance === 1 && res.difference > 3) return 'b';
if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) return 'c';
return 'd';
})();
我们可以使用 && 和 || 等基本运算符来简化它
let obj = {}
function checkWord (res) {
return (res.distance === 0) && 'a'
|| (res.distance === 1 && res.difference > 3) && 'b'
|| (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) && 'c'
|| 'd';
}
// case 1 pass
obj.distance = 0
console.log(checkWord(obj))
// case 2 pass
obj.distance = 1
obj.difference = 4
console.log(checkWord(obj))
// case 3 pass
obj.distance = 2
obj.difference = 6
obj.key = [1,2,3,4,5,6]
console.log(checkWord(obj))
// case 4 fail all cases
obj.distance = -1
console.log(checkWord(obj))
有时我们不得不(或只是喜欢)使用单行表达式或变量定义。
因此,我们可以将破坏性赋值与三元运算符结合使用。例如,
是:
const a = props.a ? props.a : cond2 ? 'val2.0' : 'val2.1' ;
让我们更新到:
const { a = cond2 ? 'val2.0' : 'val2.1' } = props;
它甚至保持相对良好的可读性。
如果你使用lodash你可以使用_.cond
免费版带lodash/fp:
const getWord = _.cond([
[_.flow(_.get('distance'), _.eq(0)), _.constant('a')],
[_.flow(_.get('distance'), _.eq(1)) && _.flow(_.get('difference'), _.gt(3)), _.constant('b')],
[
_.flow(_.get('distance'), _.eq(2))
&& _.flow(_.get('difference'), _.gt(5))
&& _.flow(_.get('key'), _.toString, _.gt(5)),
_.constant('c'),
],
[_.stubTrue, _.constant('d')],
]);
如果您想要一些可读性稍差的内容...这可能适合您。编写一个通用函数来获取一组条件(按照您编写 if/else 的顺序)和一组赋值值。使用 .indexOf() 找到您条件中的第一个真理,并 return 该索引处的赋值数组值。顺序很重要,条件需要按索引匹配您想要的分配:
const conditionalAssignment = (conditions, assignmentValues) => assignmentValues[conditions.indexOf(true)];
您可以修改以处理 truthy 而不是 struct true,并注意未定义的 return 如果 indexOf 是 -1
我个人喜欢对一行代码使用三元表达式。
虽然,我不得不同意嵌套三元表达式会导致粗略的代码。
我最近开始使用 Object
构造函数来编写更清晰的代码:
let param: "one" | "two" | "three";
// Before
let before: number = param === "one" ? 1 : param === "two" ? 2 : 3;
// After
let after: number = Object({
one: 1,
two: 2,
three: 3
})[param];
现实生活中的例子:
const opacity =
Platform.OS === "android"
? 1
: Object({
disabled: 0.3,
pressed: 0.7,
default: 1,
})[(disabled && "disabled") || (pressed && "pressed") || "default"];
ES6 对此打开了大门,对 switch 语句的不同看法。
Object.entries({
['a']: res.distance === 0,
['b']: res.distance === 1 && res.difference > 3,
['c']: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
}).filter(n => n[1] === true)[0][0]
除了if-else和switch,你也可以试试方括号。
const testName = cond1 ? [cond2 : a : b] : [cond3 ? c : d]
我个人喜欢三元运算符,依我的拙见,它们使复杂的表达式变得非常容易理解。拿这个:
const word = (distance === 0) ? 'a'
: (distance === 1 && diff > 3) ? 'b'
: (distance === 2 && diff > 5 && key.length > 5) ? 'c'
: 'd';
然而在我们项目的 ESLINT 规则中禁止嵌套的三元运算符,所以我必须摆脱上面的。
我正在尝试找出这种方法的替代方法。我真的不想把它变成一个巨大的 if / else 语句,但不知道是否还有其他选择。
您的备选方案基本上是:
- 你不想做的
if
/else
- 一个
switch
结合if
/else
我试图想出一个合理的查找地图选项,但很快就变得不合理了。
我会选择 #1,它没那么大:
if (res.distance == 0) {
word = 'a';
} else if (res.distance == 1 && res.difference > 3) {
word = 'b';
} else if (res.distance == 2 && res.difference > 5 && String(res.key).length > 5) {
word = 'c';
} else {
word = 'd';
}
如果所有的大括号和垂直大小都让您感到困扰,没有它们它几乎和条件运算符版本一样简洁:
if (res.distance == 0) word = 'a';
else if (res.distance == 1 && res.difference > 3) word = 'b';
else if (res.distance == 2 && res.difference > 5 && String(res.key).length > 5) word = 'c';
else word = 'd';
(我不提倡那样做,我从不提倡去掉大括号或将 if
后面的语句放在同一行,但其他人有不同的风格观点。)
#2 在我看来更笨重,但这可能更像是一种风格评论:
word = 'd';
switch (res.distance) {
case 0:
word = 'a';
break;
case 1:
if (res.difference > 3) {
word = 'b';
}
break;
case 2:
if (res.difference > 5 && String(res.key).length > 5) {
word = 'c';
}
break;
}
最后,我 不 提倡这一点,您可以利用 JavaScript 的 switch
在 B-syntax language family: case
语句可以是expressions,并与源代码顺序中的switch值匹配:
switch (true) {
case res.distance == 0:
word = 'a';
break;
case res.distance == 1 && res.difference > 3:
word = 'b';
break;
case res.distance == 2 && res.difference > 5 && String(res.key).length > 5:
word = 'c';
break;
default:
word = 'd';
break;
}
有多丑? :-)
如果你所有的真实条件都计算为真实值(所以如果强制为布尔值,问号和分号之间的值计算为真......)你可以使你的三元表达式 return false
作为虚假的表达。然后您可以使用按位或 (||
) 运算符将它们链接起来以测试下一个条件,直到您 return 默认值的最后一个条件。
在下面的示例中,"condsXXX" 数组表示评估条件的结果。 "conds3rd" 模拟第三个条件为真,"condsNone" 模拟没有条件为真。在实际代码中,赋值表达式中有条件 "inlined":
var conds3rd = [false, false, true];
var condsNone = [false, false, false];
var val3rd = (conds3rd[0] ? 1 : false) ||
(conds3rd[1] ? 2 : false) ||
(conds3rd[2] ? 3 : 4);
var valNone = (condsNone[0] ? 1 : false) ||
(condsNone[1] ? 2 : false) ||
(condsNone[2] ? 3 : 4);
alert(val3rd);
alert(valNone);
您的示例最终可能如下所示:
word = ((res.distance === 0) ? 'a' : false) ||
((res.distance === 1 && res.difference > 3) ? 'b' : false) ||
((res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : 'd';
附带说明一下,我不觉得这是一个好看的代码,但它非常接近于像您渴望的那样使用纯三元运算符...
根据我的口味,精心构造的嵌套三元组胜过所有那些混乱的 if 和开关:
const isFoo = res.distance === 0;
const isBar = res.distance === 1 && res.difference > 3;
const isBaz = res.distance === 2 && res.difference > 5 && String(res.key).length > 5;
const word =
isFoo ? 'a' :
isBar ? 'b' :
isBaz ? 'c' :
'd' ;
我最近才遇到这个问题,google 搜索将我带到了这里,我想分享一些我最近发现的关于这个的东西:
a && b || c
和
几乎一样a ? b : c
只要b
是真实的。如果 b
不真实,您可以使用
!a && c || b
如果 c
是真实的。
第一个表达式被评估为 (a && b) || c
,因为 &&
比 ||
具有更高的优先级。
如果 a
为真,则 a && b
的计算结果为 b
如果 b
为真,则表达式变为 b || c
,其计算结果为 b
如果它是真实的,就像 a ? b : c
如果 a
是真实的,如果 a
不是真实的那么表达式将根据需要评估为 c
。
在语句层中的 &&
和 ||
技巧以及 ?
和 ||
之间交替使用无嵌套三元 eslint 规则,即非常整洁(尽管我不建议这样做,除非没有其他出路)。
快速演示:
true ? false ? true : true ? false : true ? true ? true : false : true : true
// which is interpreted as
true ? (false ? true : (true ? false : (true ? (true ? true : false) : true))) : true
// now with the trick in alternate levels
true ? (false && true || (true ? false : (true && (true ? true : false) || true))) : true
// all of these evaluate to false btw
实际上我选择了一个 b
总是真实的例子,但如果你只是设置字符串那么这应该可以正常工作,因为甚至 '0'
具有讽刺意味的是真实的。
如果您希望将 const 与嵌套的三元表达式一起使用,您可以将三元表达式替换为函数表达式。
const res = { distance: 1, difference: 5 };
const branch = (condition, ifTrue, ifFalse) => condition?ifTrue:ifFalse;
const word = branch(
res.distance === 0, // if
'a', // then
branch( // else
res.distance === 1 && res.difference > 3, // if
'b', // then
branch( // else
res.distance === 2 && res.difference > 5, // if
'c', // then
'd' // else
)
)
);
console.log(word);
或通过解构使用命名参数...
const branch2 = function(branch) {
return branch.if ? branch.then : branch.else;
}
const fizzbuzz = function(num) {
return branch2({
if: num % 3 === 0 && num % 5 === 0,
then: 'fizzbuzz',
else: branch2({
if: num % 3 === 0,
then: 'fizz',
else: branch2({
if: num % 5 === 0,
then: 'buzz',
else: num
})
})
});
}
console.log(
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16].map(
cv => fizzbuzz(cv)
)
);
编辑
在 python if 表达式之后对其进行建模可能会更清楚:
const res = { distance: 1, difference: 5 };
const maybe = def => ({
if: expr => {
if (expr) {
return { else: () => def };
} else {
return { else: els => els };
}
}
});
const word = maybe('a').if(res.distance === 0).else(
maybe('b').if(res.distance === 1 && res.difference > 3).else(
maybe('c').if(res.distance === 2 && res.difference > 5).else('d')
)
);
console.log(word);
编辑
另一个删除嵌套 if/else 分支的编辑:
const res = { distance: 1, difference: 5 };
const makeResolvedValue = def => {
const elseProp = () => def;
return function value() {
return {
if: () => ({ else: elseProp, value })
};
}
};
const value = def => ({
if: expr => {
if (expr) {
return { else: () => def, value: makeResolvedValue(def) };
} else {
return { else: els => els, value };
}
}
});
// with branching if needed
const word = value('a').if(res.distance === 0)
.else(value('b').if(res.distance === 1 && res.difference > 3)
.else(value('c').if(res.distance === 2 && res.difference > 5)
.else('d')
)
);
console.log(word)
// implicit else option for clarity
const word2 = value('a').if(res.distance === 0)
.value('b').if(res.distance === 1 && res.difference > 3)
.value('c').if(res.distance === 2 && res.difference > 5)
.else('d');
console.log(word2);
word = (res.distance === 0) ? 'a'
: (res.distance === 1 && res.difference > 3) ? 'b'
: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
: 'd';
这是一个较旧的问题,但我会这样做...我将从默认情况开始,然后根据需要更改变量或不加改变地传递它。
var word = 'd';
word = (res.distance === 0) ? 'a' : word;
word = (res.distance === 1 && res.difference > 3) ? 'b' : word
word = (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c' : word;
我一直在为这些情况使用 switch(true) 语句。在我看来,这种语法感觉比嵌套的 if/else operators
稍微优雅一些switch (true) {
case condition === true :
//do it
break;
case otherCondition === true && soOn < 100 :
// do that
break;
}
您可以写一个 immediately invoked function expression 使其更具可读性:
const word = (() => {
if (res.distance === 0) return 'a';
if (res.distance === 1 && res.difference > 3) return 'b';
if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) return 'c';
return 'd';
})();
我们可以使用 && 和 || 等基本运算符来简化它
let obj = {}
function checkWord (res) {
return (res.distance === 0) && 'a'
|| (res.distance === 1 && res.difference > 3) && 'b'
|| (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) && 'c'
|| 'd';
}
// case 1 pass
obj.distance = 0
console.log(checkWord(obj))
// case 2 pass
obj.distance = 1
obj.difference = 4
console.log(checkWord(obj))
// case 3 pass
obj.distance = 2
obj.difference = 6
obj.key = [1,2,3,4,5,6]
console.log(checkWord(obj))
// case 4 fail all cases
obj.distance = -1
console.log(checkWord(obj))
有时我们不得不(或只是喜欢)使用单行表达式或变量定义。 因此,我们可以将破坏性赋值与三元运算符结合使用。例如,
是:
const a = props.a ? props.a : cond2 ? 'val2.0' : 'val2.1' ;
让我们更新到:
const { a = cond2 ? 'val2.0' : 'val2.1' } = props;
它甚至保持相对良好的可读性。
如果你使用lodash你可以使用_.cond
免费版带lodash/fp:
const getWord = _.cond([
[_.flow(_.get('distance'), _.eq(0)), _.constant('a')],
[_.flow(_.get('distance'), _.eq(1)) && _.flow(_.get('difference'), _.gt(3)), _.constant('b')],
[
_.flow(_.get('distance'), _.eq(2))
&& _.flow(_.get('difference'), _.gt(5))
&& _.flow(_.get('key'), _.toString, _.gt(5)),
_.constant('c'),
],
[_.stubTrue, _.constant('d')],
]);
如果您想要一些可读性稍差的内容...这可能适合您。编写一个通用函数来获取一组条件(按照您编写 if/else 的顺序)和一组赋值值。使用 .indexOf() 找到您条件中的第一个真理,并 return 该索引处的赋值数组值。顺序很重要,条件需要按索引匹配您想要的分配:
const conditionalAssignment = (conditions, assignmentValues) => assignmentValues[conditions.indexOf(true)];
您可以修改以处理 truthy 而不是 struct true,并注意未定义的 return 如果 indexOf 是 -1
我个人喜欢对一行代码使用三元表达式。 虽然,我不得不同意嵌套三元表达式会导致粗略的代码。
我最近开始使用 Object
构造函数来编写更清晰的代码:
let param: "one" | "two" | "three";
// Before
let before: number = param === "one" ? 1 : param === "two" ? 2 : 3;
// After
let after: number = Object({
one: 1,
two: 2,
three: 3
})[param];
现实生活中的例子:
const opacity =
Platform.OS === "android"
? 1
: Object({
disabled: 0.3,
pressed: 0.7,
default: 1,
})[(disabled && "disabled") || (pressed && "pressed") || "default"];
ES6 对此打开了大门,对 switch 语句的不同看法。
Object.entries({
['a']: res.distance === 0,
['b']: res.distance === 1 && res.difference > 3,
['c']: (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
}).filter(n => n[1] === true)[0][0]
除了if-else和switch,你也可以试试方括号。
const testName = cond1 ? [cond2 : a : b] : [cond3 ? c : d]