3

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.

4
  • the funny thing is, it can be even shorter: i = i < 0 ? Math.max(0, len + i) : i || 0; Commented Jun 19, 2015 at 11:35
  • @NinaScholz If i is omitted how does i < 0 get evaluated? 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. Commented Jul 10, 2021 at 17:26
  • @NinaScholz the "longer" way prevents having to deal with that error. Commented Jul 12, 2021 at 0:20

7 Answers 7

6

Original 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;
}
Sign up to request clarification or add additional context in comments.

1 Comment

To be pedantic, there is no assignment to i inside the if statements. The given ternaries are side-effect free, whereas these are combining the assignment of the left with the evaluation of the right. In this example, that's 100% okay, but a little disingenuous when teaching ternary as a concept.
1

It's 2 ternary operators, nested. You can read it like this:

i = i ? (i < 0 ? Math.max( 0, len + i ) : i) : 0;

Or, completely 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.

4 Comments

i = i ? at the beginning , is that even a check ? or is it just initializing i with a value ? i find it hard to beleive that i = i evaluates to if(i){} ! , but if thats the way it is , than i guess thats the way js works !
The i = i is from the last : i, in the ternary statement. When using the if / else structure, that else isn't necessary.
the else of if (i) ... else i = 0; (3rd grey box) is assigned to if (i < 0) and not to the outer if.
To be pedantic, there is no assignment to i inside the if statements. The given ternaries are side-effect free, whereas these are combining the assignment of the left with the evaluation of the right. In this example, that's 100% okay, but a little disingenuous when teaching ternary as a concept.
0

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.

1 Comment

I fail to see where my code is incorrect enough to justify a downvote. Especially where I have explicitly defined a new variable to represent the value of the subexpressions, as there are exactly 0 side-effects (assignment to i) in the ternary provided. Happy to fix any mistake/oversight, otherwise.
0

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

7 Comments

No it does not, there's no return statement in the OP's ternary code.
A ternary operator returns it value smartass. var x = true ? "hello" : "world"; makes x true. its a return. he wanted to know what it did. This is basically what it does. It returns the true value to the assignee.
If the OP were to blindly replace the ternary statement with your code, he'd have his function return prematurely. There's a big difference between variable assignment and returning a value.
True, but he wanted to know how it works. This is how it works if you'd post it in a function call. It helps in understanding that a ternary operation returns the value to you as if you are calling a function. It removes a lot of the magic that seemingly happens. i modified my answer to more suit your comments.
You forgot the i = calculatewhat(i).
|
0

actually this is enough:

if (i < 0) {
  return Math.max(0, len + I);
}

Comments

0

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 completely to default to the array start.

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.