1

I need to iterate through two objects and check if their keys match. The trick is, I need to ensure the keys are both lower case. I've posted about it here.

This is done easily enough if I just loop through both objects and check for matches. But that means I could have at worst a runtime till the very last key in each object.

I put a counter on it and ended up with a high number of checks:

var i = 0;

for (key in objA) {
    for (key2 in objB) {
    i++; //ends up at 185 checks
    if (key.toLowerCase() === key2.toLowerCase()) {
          console.log('match');

I'm not sure what performance differences there are with if (k in..) syntax since I can't iterate thru the inner loop.

for (key in objA) {
     if (key in objB) {
     //i = ?

What is the performance difference between these two?

Note: The lengths of the objects don't match. Basically, I'm passing in a smaller object with keys that I need to check against a larger object. For the purposes of my project, I need to check if any keys in the smaller object are IN the larger object.


I checked using date time difference between start and finish.

var start = (new Date).getTime();

//loop

var diff = (new Date).getTime() - start;

Loop 1: 4 seconds

Loop 2: 1 second

The issue is, I need some way to efficiently check two lower case keys from different sized objects using the more efficient if (k in obj) check.

Community
  • 1
  • 1
user3871
  • 12,432
  • 33
  • 128
  • 268
  • How is that a performance question? the 2nd method is incompatible with the first. It won't match different case keys. – Amit May 28 '15 at 06:38
  • Can you find some way to hash the values? That way it would be linear worst case. – user3735633 May 28 '15 at 06:40
  • Checking it the key exists with `in` will always be faster than iterating, but as noted above, it's case sensitive, and not comparable. On the other hand, this is being done on objects in memory, you'll probably never notice the difference in performance anyway – adeneo May 28 '15 at 06:41
  • 2
    Also, you could just Perf it -> http://jsperf.com/case-insensitive-keys – adeneo May 28 '15 at 06:42
  • Or `Object.keys(objA).sort().join(",") === Object.keys(objB).sort().join(",")` – adeneo May 28 '15 at 06:45
  • @adeneo: `Sort` and `join` both have to iterate over the keys. I doubt those'd be very fast compared to the OP's attempts. – Cerbrus May 28 '15 at 06:46
  • 1
    Probably not very fast, but performance shouldn't matter much here anyway, it's going to be fast no matter how it's done. – adeneo May 28 '15 at 06:48
  • Did you make sure you `break` out of the inner loop once you found a match (in the `for` -> `for` version)? – Amit May 28 '15 at 06:49
  • I would not rely on date/time as a true measure of performance. Your hardware comes into play there. – user3735633 May 28 '15 at 06:51
  • Why does the smaller object have differently cased keys anyway? – Ja͢ck May 28 '15 at 06:56
  • Here's how I'd do it -> http://jsfiddle.net/adeneo/cc1bmtp4/ – adeneo May 28 '15 at 06:58
  • your note of the smaller obj is not really impoortant, because of the commutative property of intersection. so the smaller object rules alway over the bigger one. – Nina Scholz May 28 '15 at 08:09

3 Answers3

0

First of all, you'll probably want to check if both objects have the same amount of keys:

Object.keys(objA).length === Object.keys(objB).length;

If those lengths don't match, the objects don't have the same keys.

Now, if those lengths do match, you can use your second snippet to compare the keys, meaning you only have to iterate over n keys, instead of n * n, as you do in your first snippet.

Just keep in mind that these checks are case sensitive.

Something like this should work:

function checkKeys(objA, objB){
    if(Object.keys(objA).length !== Object.keys(objB).length)
        return false;
    for (key in objA) {
        if (!(key in objB))
            return false;
    }
    return true;
};

alert(checkKeys({}, {}));
alert(checkKeys({}, {a: 1}));
alert(checkKeys({a: 1}, {a: 1}));
alert(checkKeys({a: 1}, {A: 1}));
alert(checkKeys({a: 1}, {a: 1, b: 2}));
Cerbrus
  • 70,800
  • 18
  • 132
  • 147
  • This depends on how the "key in objB" is implemented, doesnt it? It would still be n * n for successful cases correct? Or possibly even worse... Better than n * n for unsuccessful cases (possibly). I appreciate your thought on this. Trying to understand for myself as well. – user3735633 May 28 '15 at 06:44
  • The lengths don't match. Basically, I'm passing in an object with keys that I need to check against a larger object. For the purposes of my project, I need to check if any keys in the smaller object are IN the larger object. – user3871 May 28 '15 at 06:45
  • @Growler - then you can get the objects as arrays with `Object.keys` and use `Array.some` to stop iterating if a match is found – adeneo May 28 '15 at 06:46
  • @Growler: In that case, this part: `for (key in objA) { if (!(key in objB)) return false; }` is probably the most efficient, although it is case sensitive. – Cerbrus May 28 '15 at 06:47
0

How about just checking if they have that property with hasOwnProperty?

for (key in objA) {

    if (objB.hasOwnProperty(key)))
        console.log("HAS the property/key, do something");
    }
    else {
        console.log("Doesn't have the property/key, do something");    
    }
}

Easiest way to check lowercase if only one has lower keys

key = key.toLowerCase(); // before the objB check

Or before this operation, you can convert it to lowercase before this check like Jack has:

var key, keys = Object.keys(obj);
var n = keys.length;
var newobj={}
while (n--) {
    key = keys[n];
    newobj[key.toLowerCase()] = obj[key];
}

What's the best way (most efficient) to turn all the keys of an object to lower case?

Community
  • 1
  • 1
Ryan Christensen
  • 7,843
  • 1
  • 27
  • 25
  • For most practical purposes, `objB.hasOwnProperty(key)` and `(key in objB)` do the same thing. – Ja͢ck May 28 '15 at 06:55
  • The difference is hasOwnProperty checks direct properties/keys. Where in gets all inherited and object properties. i.e. you can do "toString" in {} and get it but hasOwnProperty only checks properties/keys of that specific object. Much more efficient. – Ryan Christensen May 28 '15 at 06:58
  • If you're using `{}` or `Object.create(null)` it would practically be the same, unless there's compelling evidence to be otherwise. – Ja͢ck May 28 '15 at 07:00
  • True, but if checking dictionary keys or object keys of that particular object, there is a reason hasOwnProperty exists, that is it. in can be methods, other prototype additions and all that. If you are looking just for keys added directly to that object, that was hasOwnProperty's purpose and naming as such. – Ryan Christensen May 28 '15 at 07:02
  • You're not addressing the OPs main requirement: that the check be case insensitive – harmic May 28 '15 at 07:18
  • True, updated for some lowercase handling options, I was mainly trying to get away from unnecessary looping/nesting. These are just possible directions. – Ryan Christensen May 28 '15 at 07:54
0

If one object is considerably bigger, you could transform the keys for that object first and then test the smaller object against it:

function test(big, small)
{
  // lowercase all keys in the bigger object
  var map = {};
  for (var bk in big) {
    map[bk.toLowerCase()] = true;
  }

  // test all keys in smaller object against map
  for (var sk in small) {
    if (!(sk.toLowerCase() in map)) {
      return false;
    }
  }
  return true;
}

Or this fancy way:

function test(big, small)
{
  var map = Object.keys(big).reduce(function(map, key) {
    map[key.toLowerCase()] = true;
    return map;
  }, {});

  return Object.keys(small).all(function(key) {
    return key.toLowerCase() in map;
  });
}
Ja͢ck
  • 170,779
  • 38
  • 263
  • 309