Ответ 1
Лично я считаю, что лучший способ сделать это - это старый добрый оператор if
:
var value = someArray.indexOf(3);
if (value === -1) {
value = 0;
}
Например, что-то вроде этого:
var value = someArray.indexOf(3) !== -1 ? someArray.indexOf(3) : 0
Есть ли лучший способ написать это? Опять же, я не ищу ответа на точный вопрос выше, просто пример того, когда вы могли бы повторять операнды в выражениях тернарных операторов...
Лично я считаю, что лучший способ сделать это - это старый добрый оператор if
:
var value = someArray.indexOf(3);
if (value === -1) {
value = 0;
}
Код должен быть читабельным, поэтому краткость не должна означать краткость любой ценой - для этого вы должны поместить в https://codegolf.stackexchange.com/ - поэтому вместо этого я бы рекомендовал использовать вторую локальную переменную с именем index
, чтобы максимизировать понятность чтения (с минимальными затратами времени выполнения) тоже замечу):
var index = someArray.indexOf( 3 );
var value = index == -1 ? 0 : index;
Но если вы действительно хотите сократить это выражение, потому что вы жестокий садист по отношению к своим коллегам или сотрудникам проекта, тогда вы можете использовать 4 подхода:
var
Вы можете использовать возможность оператора var
, чтобы определить (и назначить) вторую временную переменную index
, разделив ее запятыми:
var index = someArray.indexOf(3), value = index !== -1 ? index: 0;
Другой вариант - это самозапускающаяся анонимная функция:
// 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) );
Есть также печально известный "оператор запятой", который поддерживает 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.
Комментатор @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 );
Для чисел
Вы можете использовать функцию 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.
Не совсем, просто используйте другую переменную.
Ваш пример обобщает что-то вроде этого.
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)
Использовать ||
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';
Используйте рефакторинг переменных извлечения:
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;
Я лично предпочитаю два варианта:
Чисто, если, например, @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
Вероятно, вы ищете оператора коалесценции. К счастью, мы можем использовать прототип 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
Мне нравится ответ @slebetman. Комментарий в нем выражает озабоченность по поводу того, что переменная находится в "промежуточном состоянии". если это вас очень беспокоит, я предлагаю инкапсулировать его в функции:
function get_value(arr) {
var value = arr.indexOf(3);
if (value === -1) {
value = 0;
}
return value;
}
Затем просто позвоните
var value = get_value( someArray );
Вы можете выполнять более общие функции, если вы используете их в других местах, но не переусердствуйте, если это очень конкретный случай.
Но, честно говоря, я просто буду делать как @slebetman, если мне не понадобится повторное использование из нескольких мест.
Есть два способа взглянуть на ваш вопрос: вы либо хотите уменьшить длину строки, либо хотите избежать повторения переменной в тройной форме. Первое тривиально (и многие другие пользователи разместили примеры):
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
Используйте вспомогательную функцию:
function translateValue(value, match, translated) {
return value === match ? translated : value;
}
Теперь ваш код очень читабельен, и повторений нет.
var value = translateValue(someArray.indexOf(3), -1, 0);
Иерархия проблем кодирования:
Все ответы на странице пока кажутся правильными, но я думаю, что моя версия имеет самую высокую ясность, что более важно, чем краткость. Если вы не считаете вспомогательную функцию, поскольку ее можно повторно использовать, она также является наиболее кратким. Несколько похожее предложение использовать вспомогательную функцию, к сожалению, использует лямбду, которая для меня просто скрывает, что она делает. Более простая функция с одной целью, которая не принимает лямбда, просто значения, для меня намного лучше.
P.S. Если вам нравится синтаксис ES6:
const translateValue = (value, match, translated) => value === match ? translated : value;
let value = translateValue(someArray.indexOf(3), -1, 0); // or const
Я думаю, что оператор ||
может быть адаптирован к indexOf
:
var value = ((someArray.indexOf(3) + 1) || 1) - 1;
Возвращаемое значение сдвигается на 1, делая 0 от -1, что является ложным и поэтому заменяется вторым 1. Затем он сдвигается назад.
Однако, пожалуйста, имейте в виду, что читаемость превосходит возможность повторения.
Это простое решение с побитовым 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
Вы можете использовать повторное назначение:
&&
для переназначения, потому что если первое условие ложно, второе выражение не будет оцениватьсяИсх.
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);
Учитывая пример кода в вопросе, неясно, как будет определено, что 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);
Тернар похож на if-else, если вам не нужна роль else, почему бы не просто сингл, если вместо этого.
if ((value = someArray.indexOf(3)) < 0) value = 0;
Для этого конкретного случая вы можете использовать короткое замыкание с логическим оператором ||
. Поскольку 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);