0

I am trying to figure out the best way to do this. Let's say I have two Objects

const baseObject = {
   firstName: "John",
   lastName: "Doe",
   dob: "01/01/00",
   siblings: []
}

const updatedObject = {
   firstName: "Johnathan",
   lastName: "Doe",
   dob: "01/01/00",
   siblings: []
}

I want to compare my baseObject with my updatedObject while omitting dob & siblings to find any differences and the changes in another obj.

I have been struggling just how to do this. I have played with .filter, .map etc. with no ground being made.

Note: Yes the [] does actually matter.

Thanks for any help.

3
  • 1. That's not valid JS. 2. Post the code you've tried so far 3. how about if (a.firstName == b.firstName && a.lastName == b.lastName)? Commented Sep 17, 2018 at 22:24
  • @ChrisG I would prefer to not compare each object key value individually because eventually, I would expect my objects to grow. Commented Sep 17, 2018 at 22:28
  • My first thought without a workstation would be to create a function taking in params: base and updated (or just updated). Then iterate over the keys and use filter on the keys to check if values have changed. Does that make sense? Otherwise I rely on 2 way data binding toolkits/frameworks such as KnockoutJS/React/VueJS to observe changes dynamically. Commented Sep 17, 2018 at 22:29

3 Answers 3

1

Since you want to compare two objects on limited keys, either you need to specifically mention each key in if condition or you need to maintain separate memory for those keys. If you have less excluding keys then maintain an array for that else for including keys.

const baseObject = {
  firstName: "John",
  lastName: "Doe",
  dob: "01/01/00",
  siblings: []
}

const updatedObject = {
  firstName: "Johnathan",
  lastName: "Doe",
  dob: "01/01/00",
  siblings: []
}

// Excluding
function compareWithoutKeys(first = {}, second = {}, excludes = []) {
  return Object.entries(first).every(([key, value]) =>
    excludes.includes(key) ? true : second[key] === value
  )
}
compareWithoutKeys(baseObject, updatedObject, ['dob', 'siblings']) // true/false

// Including
function compareWithKeys(first = {}, second = {}, includes = []) {
  return Object.entries(first).every(([key, value]) =>
    includes.includes(key) ? second[key] === value : true
  )
}
compareWithKeys(baseObject, updatedObject, ['firstName', 'lastName']) // true/false

Update for comparing more than just strings.

In case you want to compare more than string maybe like siblings(which is an array) you have to update the comparison function, you will check the type of value and then compare it accordingly. Like if the value is an Object then compare every key or if the value is Array then compare every index. Something like:

function isEqual(first, second) {
  const firstType = Object.prototype.toString.call(first)
  const secondType = Object.prototype.toString.call(second)

  if (firstType !=== secondType) {
    return false
  }

  switch (expression) {
    case '[object Array]': return first.every((value, index) => value === second[index])
    case '[object Object]': return Object.entries(first).every((value, index) => value === second[index])
    default: return first === second
  }
}

// Excluding
function compareWithoutKeys(first = {}, second = {}, excludes = []) {
  return Object.entries(first).every(([key, value]) =>
    excludes.includes(key) ? true : isEqual(value, second[key])
  )
}
// usage
compareWithoutKeys(baseObject, updatedObject, ['dob', 'siblings']) // true/false
compareWithoutKeys(baseObject, updatedObject, ['dob']) // true/false

// Including
function compareWithKeys(first = {}, second = {}, includes = []) {
  return Object.entries(first).every(([key, value]) =>
    includes.includes(key) ? isEqual(value, second[key]) : true
  )
}
// usage
compareWithKeys(baseObject, updatedObject, ['firstName', 'lastName']) // true/false
compareWithKeys(baseObject, updatedObject, ['firstName', 'lastName', 'siblings']) // true/false

Note: This will only do first level of comparison, if you want to do deep levels comparison like maybe array of objects on which every key is an array, either you have to use a recursive function or fallback to libraries like lodash.

A recursive could be something like following, but I haven't tested the below code.

function isEqual(first, second) {
  const firstType = Object.prototype.toString.call(first)
  const secondType = Object.prototype.toString.call(second)

  if (firstType !=== secondType) {
    return false
  }

  switch (expression) {
    case '[object Array]': return first.every((value, index) => isEqual(second[index], value))
    case '[object Object]': return Object.entries(first).every((value, index) => isEqual(second[index], value))
    default: return first === second
  }
}

Article on recursive comaprison: https://gomakethings.com/check-if-two-arrays-or-objects-are-equal-with-javascript/

Lodash's isEqual: https://lodash.com/docs/4.17.10#isEqual

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

2 Comments

Is there a way to actually include siblings? If I dont exclude it the function will always return false but there are some cases where I will want to check to see if siblings are the same or not.
@Ardz you may want to see my answer for that, maybe
1

Since you have a flat object and you don't want to compare arrays inside (where sequence will matter) You can stringify them ignoring the keys you don't want and compare the strings.

const baseObject = {
    firstName: "John",
    lastName: "Doe",
    dob: "01/01/00",
    siblings: []
  },
  updatedObject1 = {
    firstName: "Johnathan",
    lastName: "Doe",
    dob: "01/01/00",
    siblings: []
  },
  updatedObject2 = {
    firstName: "John",
    lastName: "Doe",
    dob: "01/02/00",
    siblings: [10, 20]
  },
  keysToIgnore = ["dob", "siblings"],
  toJson = o => JSON.stringify(o, (k, v) => {
    if (!keysToIgnore.includes(k)) { return v }
  }),
  compare = (o1, o2) => toJson(o1)===toJson(o2);
  
  console.log('baseObject, updatedObject1: ' + compare(baseObject, updatedObject1));
  console.log('baseObject, updatedObject2: ' +compare(baseObject, updatedObject2));

2 Comments

JSON.stringify is a very expensive operation which should be avoided, and only used in case of last option left.
Well, this just another way of doing it, since the approaches with recursion or check each key separately is already added. sometime we need a cleaned approach as well, when performance is impacting or became a factor. more on any solution should be a combination of readability/cleaner approach and obviously performance.
1

I would prefer to not compare each object key value individually because eventually, I would expect my objects to grow

Finding diffs in javascript objects is not so easy if you think so. There is no magic function (I have searched this a lot) that can do it for you. That's why you have to iterate through the keys and make everything manually.

Example:

const changes = Object.keys(baseObject)
.filter(key => !['dob', 'siblings'].includes(key))
.map(key => (
  {
    property: key, 
    oldVal: baseObject[key], 
    newVal: updatedObject[key],
    wasUpdated: baseObject[key] === updatedObject[key]
  }
 ))

P.S. You did not provide what result should look like, so I wrote the code without knowing it.

Comments

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.