I was just going through the inArray method code and came across the following ::
inArray: function (elem, arr, i) {
var len;
if (arr) {
if (indexOf) {
return indexOf.call(arr, elem, i);
}
len = arr.length;
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
for (; i < len; i++) {
// Skip accessing in sparse arrays
if (i in arr && arr[i] === elem) {
return i;
}
}
}
return -1;
},
now i understand how tenary operators work , but can somebody tell me , how the below line of code really works ? is it even a ternary operator ?
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
or is it some kind of a new construct in JS ?
Thank you.
Alex-z.
I was just going through the inArray method code and came across the following ::
inArray: function (elem, arr, i) {
var len;
if (arr) {
if (indexOf) {
return indexOf.call(arr, elem, i);
}
len = arr.length;
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
for (; i < len; i++) {
// Skip accessing in sparse arrays
if (i in arr && arr[i] === elem) {
return i;
}
}
}
return -1;
},
now i understand how tenary operators work , but can somebody tell me , how the below line of code really works ? is it even a ternary operator ?
i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
or is it some kind of a new construct in JS ?
Thank you.
Alex-z.
Share Improve this question edited Jun 19, 2015 at 9:17 Tushar 87.3k21 gold badges163 silver badges181 bronze badges asked Jun 19, 2015 at 9:09 Alexander SolonikAlexander Solonik 10.3k19 gold badges86 silver badges185 bronze badges 4-
the funny thing is, it can be even shorter:
i = i < 0 ? Math.max(0, len + i) : i || 0;
– Nina Scholz Commented Jun 19, 2015 at 11:35 -
@NinaScholz If
i
is omitted how doesi < 0
get evaluated? – scign Commented Jul 9, 2021 at 4:34 -
@scign, i would be trow an error or
undefined
(here, the condition is false), depending on strict or not. – Nina Scholz Commented Jul 10, 2021 at 17:26 - @NinaScholz the "longer" way prevents having to deal with that error. – scign Commented Jul 12, 2021 at 0:20
7 Answers
Reset to default 6Original Statement:
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
To understand it better,
i = i ? (i < 0 ? Math.max(0, len + i) : i) : 0;
// ^ ^
Yes, this is nested ternary operator
? :
.
Following is the if else
representation of the above statement, represented in if..else
step by step.
if (i) {
i = i < 0 ? Math.max(0, len + i) : i;
} else {
i = 0;
}
It works as follow:
if (i) {
if (i < 0) {
i = Math.max(0, len + i);
} else {
i = i;
}
} else {
i = 0;
}
It's 2 ternary operators, nested. You can read it like this:
i = i ? (i < 0 ? Math.max( 0, len + i ) : i) : 0;
Or, pletely converted to if / else
:
if(i)
if (i < 0)
i = Math.max(0, len + i);
else
i = i;
else
i = 0;
You can shorten the if / else
structure a bit:
if(i) {
if (i < 0)
i = Math.max(0, len + i);
} else
i = 0;
Or:
if(i && i < 0)
i = Math.max(0, len + i);
if(!i)
i = 0;
This removes the redundant else i = i
. In ternary statements, an else
is required, but it can be omitted here.
Keep in mind that all the i =
assignments you seen in these if / else
statements are based on the single i =
assignment in front of the ternary operator. Ternary operators on their own (a ? b : c
) do not assign values to variables.
It's two ternaries nested.
Unrolling the outer layer would give us:
var x;
if (i) {
x = i < 0 ? Math.max( 0, len + i ) : i;
} else {
x = 0;
}
i = x;
Unwinding the inner branch, then gives us:
var x;
if (i) {
if (i < 0) {
x = Math.max( 0, len + i );
} else {
x = i;
}
} else {
x = 0;
}
i = x;
The x representing the temporary value which gets reassigned to i.
Parens might help (and parents or newlines should break these up, any time they are any harder than dirt-simple):
i = i ? ( i < 0 ? Math.max( 0, len + i ) : i ) : 0;
Now you can see the subexpression hiding in the middle of the true branch, of the outer ternary.
it breaks down to this in simple logic what kinda goes down in your mind when you code it. Note that this function does not catch undefineds, nan's nulls, strings, floats, booleans, bytes or whatever can be inputted that would be caught by the normal flow.
This is what I believe is the intention behind the line in simplified logic. This is kinda what goes on in my mind when I code such lines.
function calculatewhat(i) {
if(i != 0) {/*i = i;*/ // i gets assigned and then evaluated.
//If its anything but zero it's true, if its zero its false.
if(i < 0) { // Test if its smaller than zero
return Math.max( 0, len + i ) ;
}
else { // its bigger than 0
return i
}
else { // if its 0... but you could just as wel return i
// instead of creating a new variable for the return since i is zero.
return 0;
}
}
I would have coded it instead of the nested as follows
i = i < 0 ? Math.max( 0, len + i ) : i
And to satisfy Cerbrus this is how it really works.
function calculatewhat(i) {
if(i) { //check if its a true value. This will evaluate true also on "1", "blah",true, etc...
//To be typesafe you should make it an integer test. if(typeof i === 'number' && i !== 0);
if(i < 0) { // Test if its smaller than zero This will also return true on "-20" and -20.555
return Math.max( 0, len + i ) ;
}
else { // its bigger than 0 return i. but its type can be anything but an integer, so beware.
return i
}
else { //it's not a number or its 0.
//We can't be sure about type so lets return 0 to i making it a valid integer.
return 0;
}
}
actually this is enough:
if (i < 0) {
return Math.max(0, len + I);
}
As mentioned in other answers, this is a nested ternary. What I hope to provide here is a natural-language translation of the job this line is performing for the parent function.
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
[------|----------------------|--] inner ternary
[--|----------------------------------|---] outer ternary
Translation:
i
? ...
: 0
(Outer ternary)
If an index to start searching from i
is provided as a parameter to the function (this uses the fact that if the parameter is not provided then i
will be "falsy") then proceed to evaluate the inner ternary and update i
to the result, otherwise set i
to 0.
i < 0
? Math.max(0, len + i)
: i
(Inner ternary)
If i
is less than zero, return the the array length + i
(which, since i
is less than zero, finds the index of the element i
positions from the end of the array) with a lower bound of zero; otherwise return i
.
We can now see that this line permits the function to interpret a positive integer as a position from the start of the array and a negative integer as a position from the end of the array, while including an array bounds limitation on the position index and also allowing the parameter to be omitted pletely to default to the array start.
in the initial code, we have:
inArray:
function (elem, arr, i) {
/* [...] */
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
for (; i < len; i++) {
/* [...] */
}
return -1;
}
so basically, this:
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
can also mean: (provided that i = 5)
// i = 5 is same as 5 = i
if (5) {
if (5 < 0) {
i = Math.max(0, len + 5);
} else {
i = 5;
}
} else {
i = 0;
}
it's just an over simplified way of doing things.