【问题标题】:JavaScript validation of multiple Booleans [duplicate]多个布尔值的 JavaScript 验证 [重复]
【发布时间】:2018-12-25 07:05:12
【问题描述】:

我想验证一个具有三个布尔属性的对象,其中一个且只有一个必须为真。最好的检查方法是什么?

我想出了这个:

var t1 = obj.isFirst  ? 1 : 0,
    t2 = obj.isSecond ? 1 : 0,
    t3 = obj.isThird  ? 1 : 0;

var valid = (t1 + t2 + t3) === 1;

这很好用,但感觉有点像业余爱好者的解决方案:-)

有没有更好的方法来做到这一点?以 XOR (^) 为例?

【问题讨论】:

  • obj.isFirst + obj.isSecond + obj.isThird === 1,假设它们是布尔值而不是不同的东西。如果您愿意,可以添加显式转换(例如Number(obj.isFirst)),但 javascript 也可以隐式转换。
  • '感觉有点像业余爱好者的解决方案' 我可以向您保证,专业人士 编写的代码看起来完全像这样。不要担心你的代码看起来如何,没有人会看到它,确保编译器可以读取它并且它应该做它应该做的事情。
  • @Michiel 作为可能不得不来维护您正在编写的代码的人,请不要这样做。它只会使您的代码在未来更难阅读和维护,并提供 0 好处。
  • @Liam 这句话指的是人们关心自己的执行时间。它在这里不适用,因为这个问题是关于可读性、避免错误(通过更明显、更好阅读)和更容易重构。
  • 只是一个注释,而不是一个答案:我认为这是一个错误的模型。最好有:obj.position = (1|2|3)

标签: javascript


【解决方案1】:

您可以过滤 Object.values 并检查长度是否为 1

let obj = {
    a: true,
    b: false,
    c: false
};
let oneTrue = Object.values(obj).filter(e => e === true).length === 1;
console.log(oneTrue);

正如@deceze 评论的那样

对象中是否应该有更多属性:[obj.a, obj.b, obj.c].filter(Boolean)...


请注意,在您的情况下,向您的对象添加一个为您进行检查的方法可能是有意义的

let obj = {
    a: false,
    b: false,
    c: false,
    isValid() {
        return this.a + this.b + this.c === 1;
    }
};

console.log(obj.isValid());

【讨论】:

  • 对象中是否应该有更多的属性:[obj.a, obj.b, obj.c].filter(Boolean)...
  • 感谢@deceze 将其作为引用添加到答案中。很好的补充
  • 是的,我的对象还有其他属性,但我认为这肯定会起作用
  • 您可以像 deceze @Michiel 评论的那样接受它们。只需将 a、b、c 替换为您的属性名称并过滤它们。
  • @tika 正确,已更正
【解决方案2】:

您可以在一行中完成此操作,但不容易阅读。但如果混淆是您的目标,请继续阅读。

由于obj.isFirstobj.isSecondobj.isThird 都是01,因此您可以在它们之间使用按位异或运算符^

(obj.isFirst ^ obj.isSecond ^ obj.isThird) ^ (obj.isFirst && obj.isSecond && obj.isThird)

我们需要对obj.isFirst && obj.isSecond && obj.isThird 进行异或运算,因为obj.isFirst ^ obj.isSecond ^ obj.isThird 的计算结果为true

【讨论】:

  • 因此,这意味着 OP 必须对所有属性进行硬编码,两次
  • 但您仍然必须先定义t1t2t3。这算作答案的一部分,将其设为四行而不是您显示的一行。
  • 即使这可行,它似乎过于混淆/难以理解。至少它需要一行额外的注释来解释它的作用,这在某种程度上否定了目的。
  • @Bucket 可读性和可维护性是非爱好者代码的非常重要的标志,如果你问我...
  • 同意,见 ninja-edit
【解决方案3】:

你可以使用array.reduce:

var obj = {
    a: true,
    b: false,
    c: false
};
var res = Object.values(obj).reduce((m, o) => m + o) === 1;
console.log(res);

【讨论】:

    【解决方案4】:

    怎么样:

    var valid =  (obj.isFirst + obj.isSecond + obj.isThird) === 1;
    

    注意:求和的返回是一个整数,它会将 true 转换为 1,将 false 转换为 0。因此您实际上不需要 boolValue ? 1 : 0

    请参阅此结果集以供参考:

    console.log(true + false + false)  // 1
    console.log(false + false + false) // 0
    console.log(false + false + true)  // 1
    console.log(1 + false + 0)         // 1
    console.log(false + 1 + 1)         // 2
    

    另外让我指出,如果你想要显式转换并且你的数据是字符串,你可能会陷入这个问题。所以,我宁愿使用Boolean 而不是Number

    var a = "true",
        b = false,
        c = false;
    
    var valid = Number(a) + Number(b) + Number(c);
    console.log(valid); // NaN
    
    var a = "true",
        b = false,
        c = false;
    
    var valid = Boolean(a) + Boolean(b) + Boolean(c);
    console.log(valid); // 1
    

    【讨论】:

    • 这实际上与问题中包含的 OP 相同。
    • @Bucket 我认为不是。 OP 正在做val ? 1 : 0。我指出这一步是多余的。
    【解决方案5】:

    用途:

    obj.isFirst + obj.isSecond + obj.isThird === 1
    

    假设这些值是布尔值而不是不同的值。您可以根据需要添加显式转换,如下所示:

    Number(obj.isFirst) + Number(obj.isSecond) + Number(obj.isThird) === 1
    

    但 JavaScript 也隐式地为您进行这种转换。

    (这是基于ASDFGertecomment。)

    【讨论】:

    • 如果你真的想添加布尔值,你也可以用!!进行转换。由于true 在Javascript 中被视为数字1false0!!obj.isFirst + !!obj.isSecond + !!obj.isThird 完成相同的操作。或者,+ 转换为一个数字:+obj.isFirst + +obj.isSecond + +obj.isThird,现在我的头疼了。
    • @Bucket 在布尔值上使用!! 完全没有任何作用。
    • 检查这个:Number(1) + Number("true") + Number(true) vs Boolean(1) + Boolean("true") + Boolean(true)
    • @ASDFGerte 你是对的。但是在其他任何东西上使用!! 会非常有用。
    • @ASDFGerte 我相信!! 的目的是为了防止obj.isFirst 已经是一个等于5 的数字,例如。 if(5) { console.log("5 is true"); } 将打印到控制台。 if(!!5) 也是如此,但现在发送到 if 的参数是布尔值 true 而不是数字 5,因此现在添加将按预期工作。
    【解决方案6】:

    从另一个角度来看问题,看起来您正在尝试对可能具有 3 种可能状态的事物进行建模。不要将其表示为 3 个布尔值,将其表示为具有 3 个可能值的变量:

    const STATES = ['STATE_A', 'STATE_B', 'STATE_C'];
    let value = 'STATE_A';
    

    如果您想使用其中一个布尔值:

    doSomethingDependingOnBooleanC(value === `STATE_C`);
    

    如果您想验证 value 是否符合您的预期(您的 3 种可能状态之一):

    const isValid = STATES.includes(value);
    const isValidInOlderBrowsers = STATES.indexOf(value) !== -1;
    

    【讨论】:

    • 为了更进一步,您可以使用对象属性。 const STATES = Object.freeze({STATE_A: 1, STATE_B: 2, STATE_C: 3});let value = STATES.STATE_A;.
    • @Yay295 这正是我会做的,也许用 Symbol s 而不是数值的数字,但我不想让答案复杂化如何在 JS 中定义一个枚举。我的回答是“简化你的数据模型”,而不是“使用有效的代码”。
    【解决方案7】:

    将其包装在名为“ensureOnlyOnePropertyIsTruthy”的函数中。让它包含一个属性名称列表以及对象和你想要的任何丑陋的高性能代码。没有人需要阅读内部信息,因为您的函数为他们提供了唯一必要的上下文。

    【讨论】:

    • 我会这样做,因为我的代码中有更多地方可以使用该函数。创建一个只使用一次的函数,对我来说似乎有点过头了。
    • @Michiel 对我来说,该功能似乎很常见,可以将其填充到可以在其他项目中使用的实用程序库中。此外,函数不必重复使用。即使它只使用一次 - 它仍然提供比替代方案更好的可读性。
    • 通过将代码包装在一个函数中,您可以为您的读者提供一个简洁的“做什么”,而无需“如何”。如果他们关心“如何”,他们可以深入研究您的功能。我在自己的代码中大量使用了这个范例——我非常怀疑你只编写了可重用的函数。这会导致一些非常长的、晦涩的函数。
    猜你喜欢
    • 2023-03-31
    • 2014-01-06
    • 2014-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-03
    • 1970-01-01
    相关资源
    最近更新 更多