29

I need a function isSubset, which when given two objects compares its values and tell if one object is subset of another.

object1 = { pickUpLocation : {city : 'Hyderabad', state: 'Telangana' }};
object2 = { dist : 322, pickUpLocation:  {city : 'Hyderabad', state: 'Telangana' }};
isSubset(object1, object2); //should return true
object3 = { pickUpLocation : {city : 'Chennai', state: 'Telangana' }}
object4 = { dist : 322, pickUpLocation: {city : 'Hyderabad', state: 'Telangana' }}
isSubset(object3, object4) //should return false as city's value is different
3
  • Loop through the properties of object2, and check if the value is equal (_.isEqual) to object1. Commented Mar 2, 2016 at 3:42
  • 3
    This question may show a lack of effort or research, which is a perfectly good reason to down-vote it. However, it does not satisfy the criteria for close-voting due to seeking debugging help yet not including the minimal example etc., since it's not seeking debugging help in the first place. Close-voters, please exercise your votes carefully and in accordance with the stated criteria. Commented Mar 2, 2016 at 6:36
  • 4
    shrugs It's a request for information. Helps me. +1 Commented Apr 2, 2019 at 17:14

11 Answers 11

30

Using Lodash isMatch

_.isMatch({prop: 'object', id: 3}, {prop: 'object'})

Performs a partial deep comparison between object and source to determine if object contains equivalent property values.

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

1 Comment

Note: null is seen as an actual value. This means if you replace {prop: 'object'} with {prop: 'object', id: null}, the function will return false.
9

It's a bit late, but it might help someone looking for answer without using any external library.

isSubset = (superObj, subObj) => {
    return Object.keys(subObj).every(ele => {
        if (typeof subObj[ele] == 'object') {
            return isSubset(superObj[ele], subObj[ele]);
        }
        return subObj[ele] === superObj[ele]
    });
};

let object1 = { pickUpLocation : {city : 'Hyderabad', state: 'Telangana' }};
let object2 = { dist : 322, pickUpLocation:  {city : 'Hyderabad', state: 'Telangana' }};
console.log(isSubset(object2, object1));

let object3 = { pickUpLocation : {city : 'Chennai', state: 'Telangana' }}
let object4 = { dist : 322, pickUpLocation: {city : 'Hyderabad', state: 'Telangana' }}
console.log(isSubset(object4, object3));

Comments

6

It can be done pretty easily with lodash.

import _ from 'lodash'

const isSubset = (aSubset, aSuperset) => (
    _.every(aSubset, (val, key) => _.isEqual(val, aSuperset[key]))
)

Usage:

const object1 = { pickUpLocation: { city: 'Hyderabad', state: 'Telangana' }}
const object2 = { dist: 322, pickUpLocation:  { city: 'Hyderabad', state: 'Telangana' }}

isSubset(object1, object2)

4 Comments

This is what the accepted answer tried to do, but they misunderstood the question. +1
@hraban Just since there's not an accepted answer anymore, which are you referring to please?
@Bondolin not sure anymore, but from voting history it seems one of the negative voted ones.
Is this any different from _.isMatch(object, subset) from the accepted answer?
5

You can try to use isSubset package.

This is true

isSubset(
  { dist : 322, pickUpLocation:  {city : 'Hyderabad', state: 'Telangana' }},
  { pickUpLocation : {city : 'Hyderabad', state: 'Telangana' }}
);

This is false

isSubset(
  { dist : 322, pickUpLocation:  {city : 'Hyderabad', state: 'Telangana' }},
  { pickUpLocation : {city : 'Chennai', state: 'Telangana' }}
);

1 Comment

Is this any different from _.isMatch(object, subset) from the highest score answer?
3

The above answer using lodash has limitation and doesn't cover all edge case scenarios. I just came up with this solution that matches all scenarios

import _ from 'lodash';

isSubset(obj1, obj2) {
  let matched = true;
  _.forEach(obj1, (value, key) => {
    if(!requirements || !_.isEqual(value, obj2[key])) {
      matched = false;
      return;
    }
  });
  return matched;
}

Case 1:

const obj1 = { foo: 'bar' };
const obj2 = { foo: 'bar', baz: 'qux' };
console.log(isSubset(obj1, obj2)); // true

Case 2:

const obj1 = { foo: 'bar' };
const obj2 = { foo: 'bar' };
console.log(isSubset(obj1, obj2)); // true

Case 3:

const obj1 = { foo: 'bar', baz: 'qux' };
const obj2 = { foo: 'bar'};
console.log(isSubset(obj1, obj2)); // false

Case 4:

const obj1 = undefined;
const obj2 = undefined;
console.log(isSubset(obj1, obj2)); // true

Case 5:

const obj1 = undefined;
const obj2 = { foo: 'bar'};
console.log(isSubset(obj1, obj2)); // true

Case 6:

const obj1 = { foo: 'bar'};
const obj2 = undefiend;
console.log(isSubset(obj1, obj2)); // false

1 Comment

3 of your examples contain typo: undefiend -> undefined
0

Nima's first answer cannot be right since for a subset condition to be true, all (not just some) elements in the "smaller" set need to be contained in the "bigger" set. The rest is basically correct though, just replace some with every, and swap the two objects (big and small):

/**
 * Determine whether "small" is a subset of "big"
 * @see https://stackoverflow.com/questions/35737312/find-if-an-object-is-subset-of-another-object-in-javascript/48971177#48971177
 */
function isSubset(big, small) {
  const { every, isEqual } = _;
  return every(small, 
    (v, k) => isEqual(v, big[k])
  );
}

// test it!
var object1 = { pickUpLocation : {city : 'Hyderabad', state: 'Telangana' }};
var object2 = { dist : 322, pickUpLocation:  {city : 'Hyderabad', state: 'Telangana' }};
var a = {i:1, j:2, k:3}; var b = {i:1, j:2}; var c = {i:2, j:2};

console.log([
  isSubset(a, b),
  isSubset(a, c),
  isSubset(b, a),
  isSubset(object1, object2),
  isSubset(object2, object1)
]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>

PS: Here is another way of doing it, but it's probably slower since it copies stuff and does not stop early:

function isSubset(big, small) { 
  const { pick, isEqual } = _;
  return isEqual(pick(big, Object.keys(small)), small);
}

Comments

0

Here is a simple solution:

  import _ from "lodash"
  // is a subset of b?
  function isSubset(a, b, merge = false) {
    const assign = merge ? _.merge : _.assign;
    var c = assign(_.cloneDeep(b), a);
    return _.isEqual(b, c);
  }

Comments

0

A bit late too, but here's another solution that doesn't require any external libraries. I based this off @Akshay Kapoor's answer, but this one handles equality for Dates and other objects that define valueOf():

const isSubset = (superObj, subObj) =>
    superObj === subObj ||
    typeof superObj === "object" &&
    typeof subObj === "object" && (
        subObj.valueOf() === superObj.valueOf() /* dates & ? */ ||
        Object.keys(subObj).every(k => isSubset(superObj[k], subObj[k]))
    );

Comments

0
function isSubset(obj1, obj2) {
  const matches = [];
  for(const n1 in obj1) {
    let match = false;
    for(const n2 in obj2) {
      if(n1 == n2 && obj1[n1] == obj2[n2]) {
        match = true;
        break;
      }
    }
    matches.push(match);
  }
  return matches.every(Boolean);
}

Comments

-1
function isSubset(obj1, obj2) {
  for (var key in obj2){
     if (JSON.stringify(obj2[key]) === JSON.stringify(obj1[key]))
        return true;
  }
  return false;
}

Edit: Now is generic, but if want it more generic you should see the follow link for the comparison https://stackoverflow.com/a/1144249/5965782

4 Comments

function should be generic
Oh my mistake, check now.
Don't use string comparison to check object equality.
In any case, he does not want to check if the values of the property are the same in each object. He wants to check if the values of the properties in one object are equal to the entire other object.
-2

In English:

Is there some key in object2 which satisifies the condition that there is deep equality between its value and object1?

Write that out:

_.some(                       // Is there some key
  object2,                    // in object2
  function(val, key) {             // which satisfies the condition that
    return _.isEqual(         // there is deep equality between
      val,                    // its value and
      object1[key]                 // object1?
    );
  }
);

In shorter form:

_.some(object2, function(val, key) { return _.isEqual(val, object1[key]); })

In ES6:

_.some(object2, (val, key) => _.isEqual(val, object1[key]))

My suggestion is to read through the lodash docs and learn all the APIs, or at least the main ones. For example, this problem will be very difficult to solve on your own if you are not aware of important routines such as _.some and _.isEqual. On the other hand, if you do know them, then it is quite trivial.

4 Comments

Thank you, this is what I was looking for.
Unfortunately I couldn't upvote this answer as I don't have enough reputation yet.
This doesn't do what the original poster says? I tried object1 and object2, it returns false.
lodash every is a better solution for this.

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.