Проверка JavaScript несколькими булерами

Я хочу проверить объект, который имеет три булевых свойства, один и только один, должны быть истинными. Каков наилучший способ проверить это?

Я придумал это:

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

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

Это прекрасно работает, но это немного похоже на решение для любителей :-)

Есть ли лучший способ сделать это? Например, используя XOR (^)?

Ответы

Ответ 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);

Ответ 2

Вы можете использовать array.reduce:

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

Ответ 3

Использование:

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

Если предположить, что значения являются булевыми, а не чем-то другим. Вы можете добавить явное преобразование, если хотите:

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

Но JavaScript также неявно делает это преобразование для вас.

(Это основано на комментарии ASDFGerte.)

Ответ 4

Если обратиться к проблеме с другой стороны, похоже, вы пытаетесь смоделировать то, что может иметь 3 возможных состояния. Не представляйте его как 3 булева, представляйте его как переменную с тремя возможными значениями:

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

Если вы хотите использовать одно из булевых:

doSomethingDependingOnBooleanC(value === 'STATE_C');

Если вы хотите подтвердить, что это value ожидаемому (одно из трех возможных состояний):

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

Ответ 5

Вы можете сделать это в одной строке, но читать ее нелегко. Но если обфускация - ваша цель, читайте дальше.

Поскольку obj.isFirst, obj.isSecond и obj.isThird - все либо 0 либо 1, вы можете использовать побитовый оператор XOR ^ между ними:

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

Нам нужно XOR против obj.isFirst && obj.isSecond && obj.isThird потому что obj.isFirst ^ obj.isSecond ^ obj.isThird оценивает значение true.

Ответ 6

Как насчет:

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

Примечание. Возврат суммы является целым числом, и он переводит true в 1 и false в 0. Таким образом, вам не нужно boolValue? 1: 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

Ответ 7

Оберните его в функцию с именем "обеспечитьOnlyOnePropertyIsTruthy". Возьмите список имен свойств вместе с объектом и всякий уродливый, исполняемый код, который вы хотите внутри. Никто не должен будет читать внутренности, потому что ваша функция дает им единственный необходимый контекст.

Ответ 8

Для чего-то чуть менее загадочного:

var valid = obj.isFirst ? !(obj.isSecond || obj.isThird) : (obj.isSecond ^ obj.isThird)