26

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
kkrishnaai
  • 325
  • 1
  • 5
  • 9
  • Loop through the properties of `object2`, and check if the value is equal (`_.isEqual`) to `object1`. –  Mar 02 '16 at 03: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. –  Mar 02 '16 at 06:36
  • 3
    *shrugs* It's a request for information. Helps me. +1 – Bondolin Apr 02 '19 at 17:14

11 Answers11

29

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.

BitOfUniverse
  • 5,903
  • 1
  • 34
  • 38
  • 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`. – payne Sep 25 '21 at 03:41
8

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));
Akshay Kapoor
  • 192
  • 1
  • 8
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)
jcady
  • 3,850
  • 2
  • 21
  • 21
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' }}
);
xfg
  • 638
  • 9
  • 11
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 = undefiend;
const obj2 = undefiend;
console.log(isSubset(obj1, obj2)); // true

Case 5:

const obj1 = undefiend;
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
Nima Soroush
  • 12,242
  • 4
  • 52
  • 53
0
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

Community
  • 1
  • 1
va2ron1
  • 112
  • 1
  • 9
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);
}
Domi
  • 22,151
  • 15
  • 92
  • 122
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);
  }
mikestaub
  • 2,134
  • 4
  • 24
  • 34
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]))
    );
Derek
  • 1,466
  • 15
  • 24
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);
}
Slev7n
  • 343
  • 3
  • 14
-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.

Nima Soroush
  • 12,242
  • 4
  • 52
  • 53