Jim Hodgson
Jim Hodgson

Reputation: 133

Javascript "==" operator lies

The following confuses me greatly. As noted in the comments, the compares seem to work by themselves, but when put together they don't

The while should run for all days in the same month, then increment i by one, then start over again.

I have laced the whole sequence up with console.log to try to figure it out, but it doesn't make any sense. Everything seems to equal one another, but still fails the "==" test in the while statement.

  var i=0;
  var currentdate = 0;
  var currentmonth = 0;
  var opensmonth = 0;
  var opens = [
  { "date":"3/30/2006","zip":"30038","latitude":"33.676358","longitude":"-84.15381"},
  { "date":"4/31/2006","zip":"30519","latitude":"34.089419","longitude":"-83.94701"}
  ];
  intid = setInterval("stepthrough()", 250);
  function stepthrough() {
    //figure out first date.
    if (currentdate == 0) { // we've not been run before
      currentdate = opens[0]["date"];
      currentmonth = currentdate.split("/", 1);
      console.log("Current Month: >" + currentmonth +"<");
    }
    console.log("Current month: " + currentmonth + " And opensdate: " + opens[i]["date"].split("/", 1));

    // 
    // TWILIGHT ZONE ENTERED.
    // 
    if (currentmonth == 3 ) { 
      console.log("Current month equals 3."); // PASSES
    }
    if (opens[i]["date"].split("/", 1) == 3) {
      console.log("Opens date equals 3."); // PASSES
    }
    // BOTH THE ABOVE TESTS PASS IN CHROME AND SAFARI WHAT THE F*$K JAVASCRIPT

    while(opens[i]["date"].split("/", 1) == currentmonth) { // WHY DOESNT THIS WORK I HATE COMPUTERS
      console.log("Trying to add a point one."); 
      addpoint(i);
      i++; 
      console.log("Trying to add a point."); 
    }

    //set the date for next iteration
    currentdate = opens[i]["date"];
    currentmonth = currentdate.split("/", 1);
    console.log ("Current date is now: " + currentdate + " and current month is now: " + currentmonth);
    jQuery('div#date').text(currentdate);

    //if (i>=5000) {
    if (!opens[i]["date"]) {
      console.log("Clearing interval");
      clearInterval(intid);
      //jQuery('div#date').text("Limited at 5000 records")
    }
  }

Upvotes: 4

Views: 223

Answers (4)

Nicole
Nicole

Reputation: 33227

Please upvote nrabinowitz's answer because he was first and it is correct.

However, I do want to add some details on the underlying problem and how Javascript handles == implicit conversions between Arrays, Numbers, and Strings.

To summarize: Each type has slightly different rules when using == with different types. Arrays are converted to primitive value when compared to Number or String, but not when compared with another Array.

Detail:

  1. String.split returns an Array.
  2. Strings and Numbers are raw types in Javascript. The others are Boolean, Object, Null, and Undefined.
  3. Array is type Object
  4. == follows the "Abstract equality comparison algorithm" (x == y)
  5. In your first two comparisons, since one of the types (3) is a Number, your condition falls under the following rule:

    If Type(x) is either String or Number and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).

    In other words, it converts the array ["3"] to 3 and compares it with 3 — true (see the docs on ToPrimitive)

  6. In the last case that you say is broken, it falls under the first rule, ("Type(x) is the same as Type(y)" — they are both Object. Then it evaluates the following rule:

    Return true if x and y refer to the same object. Otherwise, return false.

  7. They contain the same value, but are not the same object (each is a different result of a call to String.split), so the result is false.

To illustrate:

console.log("3 == [3]?", 3 == [3]); // true
console.log("3 == ['3']?", 3 == ['3']);  // true
console.log("'3' == [3]?", "3" == [3]); // true
console.log("'3' == ['3']?", '3' == ['3']); // true
console.log("[3] == [3]?", [3] == [3]); // false
console.log("['3'] == ['3']?", ['3'] == ['3']); // false - NOT SAME OBJECT

var a = ['3'];
var b = a; // SAME OBJECT

console.log("a == b?", a == b); // true!

The solution, as @nrabinowitz says, is to simply add [0] to the end of the split call so that the value is the first element (a String) instead of the Array itself.

Upvotes: 1

Matt
Matt

Reputation: 41832

JavaScript typing is implicit. This means if it thinks you are trying to treat something as a number, it will do it's best to treat that object as a number, even if it is, say, a boolean, or a string.

When doing standard ==, JavaScript will use implicit conversions to try and match types. This often results in unexpected comparison results.

If you want to force strong comparisons, you must use the === operator.

That being said, if you are examining the 'number' representation of a string, e.g. "123", and want to use strong comparisons, you must convert it to a number using parseInt(str, 10);

For some examples on implicit typing in action, see the JavaScript truth table answer.

Upvotes: 7

nrabinowitz
nrabinowitz

Reputation: 55678

Here's the problem: ["1"] == 1 in Javascript, because of the implicit conversions @Matt describes. But ["1"] != ["1"] in Javascript, because you're comparing two arrays, and thus two objects, and object comparisons are only true if they point to the same object, not if they point to two identical objects.

When you assign with .split('/', 1), you're getting an array like ['3'], not the string "3" (as I think you might be assuming). So:

currentmonth = currentdate.split("/", 1); // currentmonth is ["3"]
currentmonth == 3; // true, as described above
opens[i]["date"].split("/", 1) == 3; // true, because left-hand evals to ["3"]
opens[i]["date"].split("/", 1) == currentmonth; 
// false, because you're comparing two arrays - ["3"] != ["3"]

To fix this with your current code, you can just get the string, not the array, like this:

currentmonth = currentdate.split("/")[0]; // currentmonth is "3"
opens[i]["date"].split("/")[0] == currentmonth; // true, both sides are "3"

Upvotes: 4

Nicolas Thery
Nicolas Thery

Reputation: 2448

Can you try this just to know if it's a integer vs string failure ?

This is not a nice solution but it gives a clue.

while(opens[i]["date"].split("/", 1) + "str" == currentmonth + "str")

Upvotes: 0

Related Questions