Reputation: 8841
For example, something like this:
var value = someArray.indexOf(3) !== -1 ? someArray.indexOf(3) : 0
Is there a better way to write that? Again, I am not seeking an answer to the exact question above, just an example of when you might have repeated operands in ternary operator expressions...
Upvotes: 109
Views: 26938
Reputation: 155055
Code should be readable, so being succinct should not mean being terse whatever the cost - for that you should repost to https://codegolf.stackexchange.com/ - so instead I would recommend using a second local variable named index
to maximize reading comprehensibility (with minimal runtime cost too, I note):
var index = someArray.indexOf( 3 );
var value = index == -1 ? 0 : index;
But if you really want to cut this expression down, because you're a cruel sadist to your coworkers or project collaborators, then here are 4 approaches you could use:
var
statementYou can use the var
statement's ability to define (and assign) a second temporary variable index
when separated with commas:
var index = someArray.indexOf(3), value = index !== -1 ? index: 0;
Another option is an anonymous function
which is invoked immediately after it’s defined:
// 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) );
There is also the infamous "comma operator" which JavaScript supports, which is also present in C and C++.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comma_Operator
You can use the comma operator when you want to include multiple expressions in a location that requires a single expression.
You can use it to introduce side-effects, in this case by reassigning to value
:
var value = ( value = someArray.indexOf(3), value !== -1 ? value : 0 );
This works because var value
is interpreted first (as it's a statement), and then the left-most, inner-most value
assignment, and then the right-hand of the comma operator, and then the ternary operator - all legal JavaScript.
Commentator @IllusiveBrian pointed out that the use of the comma-operator (in the previous example) is unneeded if the assignment to value
is used as a parenthesized subexpression:
var value = ( ( value = someArray.indexOf(3) ) !== -1 ? value : 0 );
Note that the use of negatives in logical expressions can be harder for humans to follow - so all of the above examples can be simplified for reading by changing idx !== -1 ? x : y
to idx == -1 ? y : x
- or idx < 0 ? y : x
.
var value = ( ( value = someArray.indexOf(3) ) == -1 ? 0 : value );
Upvotes: 109
Reputation: 20027
Use ||
const result = a ? a : 'fallback value';
is equivalent to
const result = a || 'fallback value';
If casting a
to Boolean
returns false
, result
will be assigned 'fallback value'
, otherwise the value of a
.
Be aware of the edge case a === 0
, which casts to false
and result
will (incorrectly) take 'fallback value'
. Use tricks like this at your own risk.
PS. Languages such as Swift have nil-coalescing operator (??
), which serves similar purpose. For instance, in Swift you would write result = a ?? "fallback value"
which is pretty close to JavaScript's const result = a || 'fallback value';
Upvotes: 21
Reputation: 5940
A ternary is like an if-else, if you don't need the else part, why not just a single if instead..
if ((value = someArray.indexOf(3)) < 0) value = 0;
Upvotes: 0
Reputation: 386560
This is a simple solution with bitwise NOT and a default value of -1
which results later to zero.
index = ~(~array.indexOf(3) || -1);
It works basically with a double bitwise NOT, which returns the original value or a default value, which after applying bitwise NOT returns zero.
Let's have a look to the table of truth:
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
Upvotes: 2
Reputation: 13148
Use an extract variable refactoring:
var index = someArray.indexOf(3);
var value = index !== -1 ? index : 0
It is even better with const
instead of var
. You could also do an additional extraction:
const index = someArray.indexOf(3);
const condition = index !== -1;
const value = condition ? index : 0;
In practice, use more meaningful names than index
, condition
, and value
.
const threesIndex = someArray.indexOf(3);
const threeFound = threesIndex !== -1;
const threesIndexOrZero = threeFound ? threesIndex : 0;
Upvotes: 8
Reputation: 26340
There are two ways I can see of looking at your question: you either want to reduce line length, or you specifically want to avoid repeating a variable in a ternary. The first is trivial (and many other users have posted examples):
var value = someArray.indexOf(3) !== -1 ? someArray.indexOf(3) : 0;
can be (and should be, given the function calls) shortened like so:
var value = someArray.indexOf(3);
value = value !== -1 ? value : 0;
If you are looking for a more generic solution that prevents the repetition of a variable in a ternary, like so:
var value = conditionalTest(foo) ? foo : bar;
where foo
only appears once. Discarding solutions of the form:
var cad = foo;
var value = conditionalTest(foo) ? cad : bar;
as technically correct but missing the point, then you are out of luck. There are operators, functions, and methods that possesses the terse syntax you seek, but such constructs, by definition, aren't ternary operators.
Examples:
javascript, using ||
to return the RHS when the LHS is falsey
:
var value = foo || bar; // equivalent to !foo ? bar : foo
Upvotes: 4
Reputation: 24146
I personally prefer two variants:
Pure if, like @slebetman suggested
Separate function, which replaces invalid value with default one, like in this example:
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
Upvotes: 6
Reputation: 13177
I think the ||
operator can be tailored to indexOf
:
var value = ((someArray.indexOf(3) + 1) || 1) - 1;
The returned value is shifted up by 1, making 0 from -1, which is falsey and therefore gets replaced by the second 1. Then it is shifted back.
However, please keep in mind that readability is superior to avoiding repetition.
Upvotes: 2
Reputation: 50201
Use a helper function:
function translateValue(value, match, translated) {
return value === match ? translated : value;
}
Now your code is very readable, and there's no repetition.
var value = translateValue(someArray.indexOf(3), -1, 0);
The hierarchy of coding concerns is:
All the answers on the page so far appear to be correct, but I think my version has the highest clarity, which is more important than conciseness. If you don't count the helper function—as it can be reused—it is the most concise as well. The somewhat similar suggestion to use a helper function unfortunately uses a lambda that, to me, just obscures what it's doing. A simpler function with one purpose that doesn't take a lambda, just values, is to me much better.
P.S. If you like ES6 syntax:
const translateValue = (value, match, translated) => value === match ? translated : value;
let value = translateValue(someArray.indexOf(3), -1, 0); // or const
Upvotes: 3
Reputation: 6733
I like @slebetman's answer. The comment under it express concern about the variable being in an "intermediate state". if this is a big concern for you then I suggest encapsulating it in a function:
function get_value(arr) {
var value = arr.indexOf(3);
if (value === -1) {
value = 0;
}
return value;
}
Then just call
var value = get_value( someArray );
You could do more generic functions if you have uses for them in other places, but don't over-engineer if it's a very specific case.
But to be honest I would just do as @slebetman unless I needed to re-use from several places.
Upvotes: 5
Reputation: 1078
You're probably looking for a coalescing operator. Luckily, we can leverage the Array
prototype to create one:
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
This could be further generalized to your case, by adding a parameter to the function:
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
Upvotes: 6
Reputation: 211962
Not really, just use another variable.
Your example generalizes to something like this.
var x = predicate(f()) ? f() : default;
You're testing a computed value, then assigning that value to a variable if it passes some predicate. The way to avoid re-calculating the computed value is obvious: use a variable to store the result.
var computed = f();
var x = predicate(computed) ? computed : default;
I get what you mean - it seems like there ought to be some way to do this that looks a little cleaner. But I think that's the best way (idiomatically) to do this. If you were repeating this pattern a lot in your code for some reason, you might write a little helper function:
var setif = (value, predicate, default) => predicate(value) ? value : default;
var x = setif(someArray.indexOf(3), x => x !== -1, 0)
Upvotes: 28
Reputation: 3830
For numbers
You can use the Math.max()
function.
var value = Math.max( someArray.indexOf('y'), 0 );
It will keep the boundaries of the result from 0
until the first result greater than 0
if that's the case. And if the result from indexOf
is -1
it will return 0 as is greater than -1
.
For booleans and boolean-y values
For JS there is no general rule AFAIK specially because how falsy values are evaluated.
But if something can help you most of the time is the or operator (||
):
// Instead of
var variable = this_one === true ? this_one : or_this_one;
// you can use
var variable = this_one || or_this_one;
You have to be very careful with this, because in your first example, indexOf
can return 0
and if you evaluate 0 || -1
it will return -1
because 0
is a falsy value.
Upvotes: 54
Reputation: 1
Given the example code at Question it is not clear how it would be determined that 3
is or is not set at index 0
of someArray
. -1
returned from .indexOf()
would be valuable in this instance, for the purpose of excluding a presumed non-match which could be a match.
If 3
is not included in array, -1
will be returned. We can add 1
to result of .indexOf()
to evaluate as false
for result being -1
, where followed by ||
OR
operator and 0
. When value
is referenced, subtract 1
to get index of element of array or -1
.
Which leads back to simply using .indexOf()
and checking for -1
at an if
condition. Or, defining value
as undefined
to avoid possible confusion as to actual result of evaluated condition relating to original reference.
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);
Upvotes: 0
Reputation: 42099
You could use re-assignment:
&&
operator for reassignment, because if the first condition is false, the second expression won't be evaluatedEx.
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);
Upvotes: 0
Reputation: 113866
Personally I find the best way to do this is still the good old if
statement:
var value = someArray.indexOf(3);
if (value === -1) {
value = 0;
}
Upvotes: 176