简体   繁体   中英

How to check if an object's keys and deep keys are equal, similar to lodash's isEqual?

So I'm in a unique situation where I have two objects, and I need to compare the keys on said objects to make sure they match the default object. Here's an example of what I'm trying to do:

const _ = require('lodash');

class DefaultObject {
  constructor(id) {
    this.id = id;
    this.myobj1 = {
      setting1: true,
      setting2: false,
      setting3: 'mydynamicstring'
    };
    this.myobj2 = {
      perm1: 'ALL',
      perm2: 'LIMITED',
      perm3: 'LIMITED',
      perm4: 'ADMIN'
    };
  }
}

async verifyDataIntegrity(id, data) {
  const defaultData = _.merge(new DefaultObject(id));
  if (defaultData.hasOwnProperty('myoldsetting')) delete defaultData.myoldsetting;
  if (!_.isEqual(data, defaultData)) {
    await myMongoDBCollection.replaceOne({ id }, defaultData);
    return defaultData;
  } else {
    return data;
  }
}

async requestData(id) {
  const data = await myMongoDBCollection.findOne({ id });
  if (!data) data = await this.makeNewData(id);
  else data = await this.verifyDataIntegrity(id, data);
  return data;
}

Let me explain. First, I have a default object which is created every time a user first uses the service. Then, that object is modified to their customized settings. For example, they could change 'setting1' to be false while changing 'perm2' to be 'ALL'.

Now, an older version of my default object used to have a property called 'myoldsetting'. I don't want newer products to have this setting, so every time a user requests their data I check if their object has the setting 'myoldsetting', and if it does, delete it. Then, to prevent needless updates (because this is called every time a user wants their data), I check if it is equal with the new default object.

But this doesn't work, because if the user has changed a setting, it will always return false and force a database update, even though none of the keys have changed. To fix this, I need a method of comparing the keys on an object, rather any the keys and data.

That way, if I add a new option to DefaultObject, say, 'perm5' set to 'ADMIN', then it will update the user's object. But, if their object has the same keys (it's up to date), then continue along your day.

I need this comparison to be deep, just in case I add a new property in, for example, myobj1. If I only compare the main level keys (id, myobj1, myobj2), it won't know if I added a new key into myobj1 or myobj2.

I apologize if this doesn't make sense, it's a very specific situation. Thanks in advance if you're able to help.

~~~~EDIT~~~~

Alright, so I've actually come up with a function that does exactly what I need. The issue is, I'd like to minify it so that it's not so big. Also, I can't seem to find a way to check if an item is a object even when it's null. This answer wasn't very helpful.

Here's my working function.

function getKeysDeep(arr, obj) {
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === 'object') {
      arr = getKeysDeep(arr, obj[key]);
    }
  });
  arr = arr.concat(Object.keys(obj));
  return arr;
}

Usage

getKeysDeep([], myobj);

Is it possible to use it without having to put an empty array in too?

So, if I understand you correctly you would like to compare the keys of two objects, correct?

If that is the case you could try something like this:

function hasSameKeys(a, b) {
  const aKeys = Object.keys(a);
  const bKeys = Object.keys(b);
  return aKeys.length === bKeys.length && !(aKeys.some(key => bKeys.indexOf(key) < 0));
}

Object.keys(x) will give you all the keys of the objects own properties.

indexOf will return a -1 if the value is not in the array that indexOf is being called on.

some will return as soon as the any element of the array (aKeys) evaluates to true in the callback. In this case: If any of the keys is not included in the other array ( indexOf(key) < 0 )

Alright, so I've actually come up with a function that does exactly what I need. The issue is, I'd like to minify it so that it's not so big. Also, I can't seem to find a way to check if an item is a object even when it's null.

In the end, this works for me. If anyone can improve it that'd be awesome.

function getKeysDeep(obj, arr = []) {
  Object.keys(obj).forEach(key => {
    if (typeof obj[key] === 'object' && !Array.isArray(obj[key]) && obj[key] !== null) {
      arr = this.getKeysDeep(obj[key], arr);
    }
  });
  return arr.concat(Object.keys(obj));
}
getKeysDeep(myobj);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM