4

I have some code that where a variable could be undefined, null, or a normal value. The code needs to do the same thing regardless of whether the variable is undefined or null. Is there a danger to saying

for (var cur = this.buckets[i]; cur != null; cur = cur.next) {

instead of

for (var cur = this.buckets[i]; cur !== undefined && cur !== null; cur = cur.next) {

The full program is below (the line in question is in HashTable.prototype.walk).

var hash_seed = Math.floor(Math.random() * 256);

function jenkins_hash(key, interval_size) {
    var hash = hash_seed;
    for (var i=0; i<key.length; ++i) {
        hash += key.charCodeAt(i);
        hash += (hash << 10);
        hash ^= (hash >> 6);
    }
    hash += (hash << 3);
    hash ^= (hash >> 11);
    hash += (hash << 15);
    // make unsigned and modulo interval_size
    return (hash >>> 0) % interval_size;
}

//constructor, takes the number of buckets to create
function HashTable(size) {
    this.buckets = new Array(size);
}

//private method, ignore
HashTable.prototype._position = function(key) {
    var index = jenkins_hash(key, this.buckets.length);
    var cur   = this.buckets[index];

    if (cur === undefined) {  
          return { i: index, cur: null, prev: null };
    }

    var prev = cur;
    for (; cur !== null; cur = cur.next) {
        if (cur.key == key) {
            return { i: index, cur: cur, prev: prev };
        }
        prev = cur;
    }

    return { i: index, cur: cur, prev: prev };
};

// associate a value with a key in the hash
HashTable.prototype.store = function(key, value) {
    var r = this._position(key);
    if (r.prev === null) {  
          this.buckets[r.i] = {
              key: key, value: value, next: null  
          };
          return;
    }

    if (r.cur !== null) {
        r.cur.value = value;
        return;
    }

    r.prev.next = {
        key: key, value: value, next: null  
    };
    return;
};

// fetches the value associated with the key
// returns undefined if the key is not in the hash
HashTable.prototype.fetch = function(key) {
    var r = this._position(key);
    if (r.cur === null) {
        return undefined;
    }

    return r.cur.value;
};

// returns true if the key is in the hash
// returns false if the key isn't in the hash
HashTable.prototype.exists = function(key) {
    var r = this._position(key);
    return r.cur !== null;
};

// removes a key from the hash
HashTable.prototype.delete = function(key) {
    var r = this._position(key);
    if (r.cur === null) {
        return;  
    }
    if (r.cur === r.prev) {
        this.buckets[r.i] = r.cur.next;  
    }
    r.prev.next = r.cur.next;
};

// removes all keys from the hash
HashTable.prototype.clear = function() {
    var length   = this.buckets.length;
    this.buckets = new Array(length);
};

// run a funciton for every key/value pair in the hash
// function signature should be function(k, v) {}
// WARNING: adding keys to the hash while this method is
// running may lead to unexpected results
HashTable.prototype.walk = function(func) {
    for (var i = 0; i < this.buckets.length; i++) {
        for (var cur = this.buckets[i]; cur != null; cur = cur.next) {
            func(cur.key, cur.value);
        }  
    }  
};

// returns all of the keys in the hash
HashTable.prototype.keys = function() {
    var keys = [];
    this.walk(function(k,v) { keys.push(k) });
    return keys;
};

// run a function for every key/value pair in the hash
// function signature should be function(k, v) {}
// WARNING: only keys in the hash when the method is called
// will be visited; however, changes to their values will be
// reflected
HashTable.prototype.safer_walk = function(func) {
    var keys = this.keys();
    for (var i = 0; i < keys.length; i++) {
        func(keys[i], this.fetch(keys[i]));
    }
}

var h = new HashTable(101);

h.store("abc", 5);
h.store("def", 6);
h.walk(function(k, v) { console.log(k + " => " + v) });
3
  • Simply place a comment that comparison using the != operator was correct - that would be sufficient in my opinion. Having said that though, I'm not sure this is really something for SO to answer. Commented May 6, 2013 at 14:22
  • @Qantas94Heavy If I have to add a comment to make it clear to other programmers, then I should probably just change it to the latter. Commented May 6, 2013 at 14:26
  • It's not that it's not clear (as Pointy said, the algorithm is in the specification) - it's that they might believe that you were intending to only compare to null, not undefined and null. Commented May 6, 2013 at 14:29

1 Answer 1

5

The semantics of != are well-defined. If you compare to null, then:

  • if "cur" is undefined or null, then the result is false
  • if "cur" is anything else, then the result is true

The "abstract" equality comparison (like the strict comparison) first checks the type of each operand (which, note well, is different from what typeof returns!). The type of null is the Null type, and the type of undefined is the Undefined type. The abstract comparison algorithm explicitly considers undefined and null to be equal.

Thus there's really no point doing your own explicit check for both null and undefined. (You may want to check separately of course if you need logic that's different from the abstract comparison built into the language.)

Sign up to request clarification or add additional context in comments.

3 Comments

boy did I confuse myself trying to summarize === for this :-)
If I am reading what you wrote correctly, then there is no danger: the two statements are functionally identical (and I can ignore the warning my editor is throwing).
@Chas.Owens yes I just added a little more stuff. The spec is here.

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.