Альтернатива вложенному тернарному оператору в JS

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

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

Однако в нашем проекте правила ESLINT запрещены вложенные троичные операторы, поэтому я должен избавиться от вышеуказанного.

Я пытаюсь найти альтернативы этому подходу. Я действительно не хочу превращать его в огромный оператор if/else, но не знаю, есть ли другие варианты.

Ответ 1

Ваши альтернативы здесь в основном:

  • Это if/else, которое вы не хотите делать
  • A 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 могут быть выражениями и сопоставляются с значением переключателя в порядке исходного кода:

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;
}

Как уродливо это?: -)

Ответ 2

Если все ваши истинные условия оцениваются с помощью правдивых значений (поэтому значение между вопросительным знаком и точкой с запятой оценивается как true, если принудительно булевать...), вы можете заставить ваши тернарные выражения возвращать false в качестве фальшивого выражения. Затем вы можете связать их с побитовым или (||) оператором, чтобы проверить следующее условие, до последнего, где вы вернете значение по умолчанию.

В приведенном ниже примере массив "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);

Ответ 3

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

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

Ответ 4

Если вы хотите использовать 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);

// or using named parameter via destructuring

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)
  )
);

Ответ 5

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;

Ответ 6

Я столкнулся с этим совсем недавно, и поисковик 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' иронически правдоподобно.

Ответ 7

Я использовал оператор switch (true) для этих случаев. По-моему, этот синтаксис выглядит немного более элегантным, чем вложенные операторы if/else

switch (true) {
  case condition === true :
    //do it
    break;
  case otherCondition === true && soOn < 100 :
    // do that
    break;
}