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

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

  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, но не знаю, есть ли другие варианты.

8 ответов


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

  1. это if/else вы не хотите делать
  2. 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-семейство языков синтаксиса: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;
}

насколько это уродливо? :-)


на мой вкус, тщательно структурированная вложенная тройка бьет все эти грязные 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' ;

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

в приведенном ниже примере массив "condsXXX" представляет собой результат оценки условия. "conds3rd" имитирует 3-е условие является "истиной" и "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';

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


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;

вы могли бы написать сразу же вызывается функция-выражение чтобы сделать его немного более читаемым:

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';
})();

ссылка на repl


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

... edit: возможно, будет яснее моделировать его после python, если выражение таково:

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

я столкнулся с этим слишком недавно, и поиск google привел меня сюда, и я хочу поделиться тем, что я недавно обнаружил в отношении этого:

a && b || c

это почти то же самое как

a ? b : c

пока b - истина. Если b это не правда, вы можете обойти это, используя

!a && c || b

если c - истина.

первое выражение оценивается как (a && b) || c as && имеет больший приоритет, чем ||.

если a - истина тогда a && b возвращает b Если b истинно, поэтому выражение становится b || c который оценивает в b если это правда, так же, как a ? b : c если бы a это правда, и если a не является правдивым, тогда выражение будет оцениваться как c по мере необходимости.

чередуя && и || хитрость и ? и || в слоях трюков оператора правило no-nested-ternary 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' по иронии судьбы является истина.


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

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