Sam
Sam

Reputation: 14596

javascript: check if complex object is in array

Consider the following code:

var queryParamCache = {};

function doThing(filter, offset)
{
  var cacheObj = {filter:filter, offset: offset};

  if (queryParamCache[cacheObj]) {
    //do something
  }
  else{
    queryParamCache[cacheObj] = true;
   //do something else
  }
}

as soon as queryParamCache[cacheObj] is set to true, then whenever this function is called it will always be true, no matter the values of cacheObj.

I believe it's because the check is against [object, object] instead of the actual values of filter and offset.

How do I achieve what I want ?

Upvotes: 2

Views: 219

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074989

When you do this:

queryParamCache[cacheObj]

you're actually doing this:

queryParamCache[String(cacheObj)]

which is (on generic objects)

queryParamCache["[object Object]"]

regardless of what's in cacheObj.

Consider: Live Copy | Live Source

var a = {foo: "bar"};
var b = {bar: "charlie"};
var cache = {};

cache[a] = "Hi there";
console.log(cache[b]);  // "Hi there"

Both a and b get turned into the string "[object Object]".

How do I achieve what I want ?

If your goal is to have queryParamCache be a store of values that you can look up by using an object, you'll have to define a mechanism for turning that object into a string. Property names (keys) in JavaScript objects are always (for now) strings.

One way you can do that is to give your objects a different toString method, like this: Live Copy | Live Source

function CacheEntry(filter, offset) {
  this.filter = filter;
  this.offset = offset;
}
CacheEntry.prototype.toString = function() {
  return "[Cache: " +
    this.filter + "|" +
    this.offset + "]";
};
var a = new CacheEntry("transform", 10);
var b = new CacheEntry("opacity", 22);
var cache = {};

cache[a] = "Hi there";
console.log("cache[a] = " + cache[a]); // "cache[a] = Hi there"
console.log("cache[b] = " + cache[b]); // "cache[b] = undefined"

There I've used a constructor function to assign a prototype to the objects created by the function, where that prototype has a replacement for toString. Constructor functions are just one way to assign prototypes; the other (new with ES5, although it can be shimmed) is Object.create.

Or of course, you could just assign toString to each object individually: Live Copy | Live Source

function cacheEntryToString() {
  return "[Cache: " +
    this.filter + "|" +
    this.offset + "]";
}
var a = {filter: "transform", offset: 10};
a.toString = cacheEntryToString;
var b = {filter: "opacity", offset: 22};
b.toString = cacheEntryToString;
var cache = {};

cache[a] = "Hi there";
console.log("cache[a] = " + cache[a]);
console.log("cache[b] = " + cache[b]);

...but this is what prototypes are for, so it probably makes sense to use a prototype, either via a constructor function or Object.create.

Upvotes: 4

Related Questions