Как написать выражение тернарного оператора (aka if), не повторяя себя

Например, что-то вроде этого:

var value = someArray.indexOf(3) !== -1 ? someArray.indexOf(3) : 0

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

Ответ 1

Лично я считаю, что лучший способ сделать это - это старый добрый оператор if:

var value = someArray.indexOf(3);
if (value === -1) {
  value = 0;
}

Ответ 2

Код должен быть читабельным, поэтому краткость не должна означать краткость любой ценой - для этого вы должны поместить в https://codegolf.stackexchange.com/ - поэтому вместо этого я бы рекомендовал использовать вторую локальную переменную с именем index, чтобы максимизировать понятность чтения (с минимальными затратами времени выполнения) тоже замечу):

var index = someArray.indexOf( 3 );
var value = index == -1 ? 0 : index;

Но если вы действительно хотите сократить это выражение, потому что вы жестокий садист по отношению к своим коллегам или сотрудникам проекта, тогда вы можете использовать 4 подхода:

1: временная переменная в выражении var

Вы можете использовать возможность оператора var, чтобы определить (и назначить) вторую временную переменную index, разделив ее запятыми:

var index = someArray.indexOf(3), value = index !== -1 ? index: 0;

2: Самостоятельно выполняющаяся анонимная функция

Другой вариант - это самозапускающаяся анонимная функция:

// Traditional syntax:
var value = function( x ) { return x !== -1 ? x : 0 }( someArray.indexOf(3) );

// ES6 syntax:
var value = ( x => x !== -1 ? x : 0 )( someArray.indexOf(3) );

3: оператор запятой

Есть также печально известный "оператор запятой", который поддерживает JavaScript, который также присутствует в C и C++.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator

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

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

var value = ( value = someArray.indexOf(3), value !== -1 ? value : 0 );

Это работает, потому что var value интерпретируется сначала (как оператор), а затем - самое левое, самое внутреннее назначение value, а затем правая часть оператора запятой и затем троичный оператор - весь законный JavaScript.

4: переназначить в подвыражении

Комментатор @IllusiveBrian указал, что использование оператора запятой (в предыдущем примере) не требуется, если в качестве подвыражения в скобках используется присвоение value:

var value = ( ( value = someArray.indexOf(3) ) !== -1 ? value : 0 );

Обратите внимание, что использование негативов в логических выражениях может быть труднее для людей, поэтому все приведенные выше примеры можно упростить для чтения, изменив idx !== -1 ? x : y на idx == -1 ? y : x:

var value = ( ( value = someArray.indexOf(3) ) == -1 ? 0 : value );

Ответ 3

Для чисел

Вы можете использовать функцию Math.max().

var value = Math.max( someArray.indexOf('y'), 0 );

Он сохранит границы результата от 0 до тех пор, пока первый результат будет больше, чем 0, если это произойдет. И если результат indexOf равен -1, он вернет 0, как больше, чем -1.

Для значений boolean и boolean-y

Для JS нет общего правила AFAIK специально, потому что оцениваются значения falsy.

Но если что-то может вам помочь большую часть времени, это оператор or (||):

// Instead of
var variable = this_one === true ? this_one : or_this_one;
// you can use
var variable = this_one || or_this_one;

Вы должны быть очень осторожны с этим, потому что в первом примере indexOf может возвращать 0, и если вы оцениваете 0 || -1, он вернет -1, потому что 0 является falsy.

Ответ 4

Не совсем, просто используйте другую переменную.

Ваш пример обобщает что-то вроде этого.

var x = predicate(f()) ? f() : default;

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

var computed = f();
var x = predicate(computed) ? computed : default;

Я понимаю, что вы имеете в виду - кажется, что должен быть какой-то способ сделать это, что выглядит немного чище. Но я думаю, что лучший способ (идиоматически) сделать это. Если вы почему-то повторяли этот шаблон в своем коде, вы можете написать небольшую вспомогательную функцию:

var setif = (value, predicate, default) => predicate(value) ? value : default;
var x = setif(someArray.indexOf(3), x => x !== -1, 0)

Ответ 5

EDIT: вот это предложение для Nullary-coalescing теперь в JavaScript!


Использовать ||

const result = a ? a : 'fallback value';

эквивалентно

const result = a || 'fallback value';

Если литье a в Boolean возвращает false, result будет назначено 'fallback value', в противном случае значение a.


Помните о краевом футляре a === 0, который отбрасывает на false и result будет (неправильно) принимать 'fallback value'. Используйте трюки, подобные этому, на свой страх и риск.


PS. Языки, такие как Swift, nil-coalescing оператор (??), который выполняет аналогичную цель. Например, в Swift вы напишете result = a ?? "fallback value", который довольно близок к JavaScript const result = a || 'fallback value';

Ответ 6

Используйте рефакторинг переменных извлечения:

var index = someArray.indexOf(3);
var value = index !== -1 ? index : 0

Это лучше даже с const вместо var. Вы также можете выполнить дополнительное извлечение:

const index = someArray.indexOf(3);
const condition = index !== -1;
const value = condition ? index : 0;

На практике используйте более значимые имена, чем index, condition и value.

const threesIndex = someArray.indexOf(3);
const threeFound = threesIndex !== -1;
const threesIndexOrZero = threeFound ? threesIndex : 0;

Ответ 7

Я лично предпочитаю два варианта:

  • Чисто, если, например, @slebetman предложил

  • Отдельная функция, которая заменяет недопустимое значение по умолчанию, как в этом примере:

function maskNegative(v, def) {
  return v >= 0 ? v : def;
}

Array.prototype.indexOfOrDefault = function(v, def) {
  return maskNegative(this.indexOf(v), def);
}

var someArray = [1, 2];
console.log(someArray.indexOfOrDefault(2, 0)); // index is 1
console.log(someArray.indexOfOrDefault(3, 0)); // default 0 returned
console.log(someArray.indexOfOrDefault(3, 123)); // default 123 returned

Ответ 8

Вероятно, вы ищете оператора коалесценции. К счастью, мы можем использовать прототип Array, чтобы создать его:

Array.prototype.coalesce = function() {
    for (var i = 0; i < this.length; i++) {
        if (this[i] != false && this[i] != null) return this[i];
    }
}

[null, false, 0, 5, 'test'].coalesce(); // returns 5

Это может быть дополнительно обобщено на ваш случай, добавив параметр к функции:

Array.prototype.coalesce = function(valid) {
    if (typeof valid !== 'function') {
        valid = function(a) {
            return a != false && a != null;
        }
    }

    for (var i = 0; i < this.length; i++) {
        if (valid(this[i])) return this[i];
    }
}

[null, false, 0, 5, 'test'].coalesce(); // still returns 5
[null, false, 0, 5, 'test'].coalesce(function(a){return a !== -1}); // returns null
[null, false, 0, 5, 'test'].coalesce(function(a){return a != null}); //returns false

Ответ 9

Мне нравится ответ @slebetman. Комментарий в нем выражает озабоченность по поводу того, что переменная находится в "промежуточном состоянии". если это вас очень беспокоит, я предлагаю инкапсулировать его в функции:

function get_value(arr) {
   var value = arr.indexOf(3);
   if (value === -1) {
     value = 0;
   }
   return value;
}

Затем просто позвоните

var value = get_value( someArray );

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

Но, честно говоря, я просто буду делать как @slebetman, если мне не понадобится повторное использование из нескольких мест.

Ответ 10

Есть два способа взглянуть на ваш вопрос: вы либо хотите уменьшить длину строки, либо хотите избежать повторения переменной в тройной форме. Первое тривиально (и многие другие пользователи разместили примеры):

var value = someArray.indexOf(3) !== -1 ? someArray.indexOf(3) : 0;

может быть (и должен быть, если вызов функции) укорочен так:

var value = someArray.indexOf(3);
value = value !== -1 ? value : 0;

Если вы ищете более общее решение, которое предотвращает повторение переменной в тройном порядке, например:

var value = conditionalTest(foo) ? foo : bar;

где foo появляется только один раз. Отбрасывая решения вида:

var cad = foo;
var value = conditionalTest(foo) ? cad : bar;

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

Примеры:

javascript, используя ||, чтобы вернуть RHS, когда LHS falsey:

var value = foo || bar; // equivalent to !foo ? bar : foo

Ответ 11

Используйте вспомогательную функцию:

function translateValue(value, match, translated) {
   return value === match ? translated : value;
}

Теперь ваш код очень читабельен, и повторений нет.

var value = translateValue(someArray.indexOf(3), -1, 0);

Иерархия проблем кодирования:

  • Исправить (включая настоящую производительность или проблемы SLA).
  • Очистить
  • Сжатый
  • Fast

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

P.S. Если вам нравится синтаксис ES6:

const translateValue = (value, match, translated) => value === match ? translated : value;
let value = translateValue(someArray.indexOf(3), -1, 0); // or const

Ответ 12

Я думаю, что оператор || может быть адаптирован к indexOf:

var value = ((someArray.indexOf(3) + 1) || 1) - 1;

Возвращаемое значение сдвигается на 1, делая 0 от -1, что является ложным и поэтому заменяется вторым 1. Затем он сдвигается назад.

Однако, пожалуйста, имейте в виду, что читаемость превосходит возможность повторения.

Ответ 13

Это простое решение с побитовым NOT и значением по умолчанию -1, которое будет потом до нуля.

index = ~(~array.indexOf(3) || -1);

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

Посмотрим на таблицу истины:

 indexOf    ~indexOf   boolean    default     value      result         comment
---------  ---------  ---------  ---------  ---------  ---------  ------------------
      -1          0     falsy          -1         -1          0   take default value
       0         -1    truthy                     -1          0
       1         -2    truthy                     -2          1
       2         -3    truthy                     -3          2

Ответ 14

Вы можете использовать повторное назначение:

  • инициализировать переменную до одного значения
  • используйте сериализацию оператора && для переназначения, потому что если первое условие ложно, второе выражение не будет оцениваться

Исх.

var value = someArray.indexOf(3);
value == -1 && (value=0);

var someArray = [4,3,2,1];

var value = someArray.indexOf(1);
value == -1 && (value=0);
console.log('Found:',value);

var value = someArray.indexOf(5);
value == -1 && (value=0);
console.log('Not Found:',value);

Ответ 15

Учитывая пример кода в вопросе, неясно, как будет определено, что 3 установлен или не установлен в index 0 of someArray. -1, возвращаемый из .indexOf(), был бы ценным в этом случае с целью исключения предполагаемого несоответствия, которое могло бы быть совпадением.

Если 3 не включен в массив, возвращается -1. Мы можем добавить 1 к результату .indexOf() для оценки как false для результата -1, где следует оператор || OR и 0. Когда ссылается value, вычитайте 1, чтобы получить индекс элемента массива или -1.

Это приводит к простому использованию .indexOf() и проверке на -1 в состоянии if. Или, определяя value как undefined, чтобы избежать возможной путаницы в отношении фактического результата оцениваемого условия, относящегося к исходной ссылке.

var someArray = [1,2,3];
var value = someArray.indexOf(3) + 1 || 1;
console.log(value -= 1);

var someArray = [1,2,3];
var value = someArray.indexOf(4) + 1 || 1;
// how do we know that `4` is not at index `0`?
console.log(value -= 1);

var someArray = [1,2,3];
var value = someArray.indexOf(4) + 1 || void 0;
// we know for certain that `4` is not found in `someArray`
console.log(value, value = value || 0);

Ответ 16

Тернар похож на if-else, если вам не нужна роль else, почему бы не просто сингл, если вместо этого.

if ((value = someArray.indexOf(3)) < 0) value = 0;

Ответ 17

Для этого конкретного случая вы можете использовать короткое замыкание с логическим оператором ||. Поскольку 0 считается ложным, вы можете +1 внести в свой индекс, таким образом, если index+1 равен 0, вы получите правую часть возврата в качестве результата, в противном случае вы получите ваш index+1. Затем вы можете -1 из этого результата получить ваш индекс:

const someArray = [1, 2, 3, 4];
const v = ((someArray.indexOf(3)+1) || 1)-1;
console.log(v);