比较JavaScript中的嵌套对象并返回键相等

时间:2019-04-09 10:52:06

标签: javascript

我有两个嵌套对象obj1obj2,我想将它们进行比较,然后递归地返回一个对象,该对象的每个嵌套键都有一个类似相等的布尔值标志

因此对于给定的obj1喜欢

obj1 = {
  prop1: 1,
  prop2: "foo",
  prop3: {
    prop4: 2,
    prop5: "bar" 
  }
}

obj2之类的

obj2 = {
      prop1: 3,
      prop2: "foo",
      prop3: {
        prop4: 2,
        prop5: "foobar" 
      },
      prop6: "new"
    }

它应该返回

equality = {
     prop1: false,
     prop2: true,
     prop3 : {
       prop4: true,
       prop5: false
     },
     prop6: false
   }

如果对象具有新属性,例如obj2.prop6,则等式为equality.prop6 = false

对于非嵌套对象,这里是一个简单的键比较解决方案Get the property of the difference between two objects in javascript 在递归比较嵌套对象时,此处显示JavaScript: Deep comparison recursively: Objects and properties

5 个答案:

答案 0 :(得分:5)

如果两个值都是对象,则可以迭代所有键并检查嵌套的对象。

const isObject = v => v && typeof v === 'object';

function getDifference(a, b) {
    return Object.assign(...Array.from(
        new Set([...Object.keys(a), ...Object.keys(b)]),
        k => ({ [k]: isObject(a[k]) && isObject(b[k])
            ? getDifference(a[k], b[k])
            : a[k] === b[k]
        })
    ));
}

var obj1 = { prop1: 1, prop2: "foo", prop3: { prop4: 2, prop5: "bar" } },
    obj2 = { prop1: 3, prop2: "foo", prop3: { prop4: 2, prop5: "foobar" }, prop6: "new" };

console.log(getDifference(obj1, obj2));

答案 1 :(得分:2)

浏览每个键并比较属性。如果该属性是一个对象,则递归比较这些属性。这将适用于任何级别的嵌套。由于两个对象之一value || {}可能缺少属性,因此添加了检查。

const obj1={prop1:1,prop2:"foo",prop3:{prop4:2,prop5:"bar"},prop7:{pro8:"only in 1"}},
    obj2={prop1:3,prop2:"foo",prop3:{prop4:2,prop5:"foobar"}, prop6: "only in 2"};
    
const isObject = val => val && typeof val === 'object'; // required for "null" comparison

function compare(obj1, obj2) {
  let equality = {},
      merged = { ...obj1, ...obj2 }; // has properties of both

  for (let key in merged) {
    let value1 = obj1[key], value2 = obj2[key];

    if (isObject(value1) || isObject(value2)) {
      equality[key] = compare(value1 || {}, value2 || {});
    } else {
      equality[key] = value1 === value2
    }
  }

  return equality;
}

console.log(compare(obj1, obj2))

答案 2 :(得分:1)

您可以使用reduce来构建新对象,并可以使用另一种get方法通过string从其他对象中获取嵌套道具,并将其与第一个对象中的当前道具值进行比较。

const obj1 = { prop1: 1, prop2: "foo", prop3: { prop4: 2, prop5: "bar" } }
const obj2 = { prop1: 3, prop2: "foo", prop3: { prop4: 2, prop5: "foobar" } }

function get(obj, path) {
  return path.split('.').reduce((r, e) => {
    if (!r) return r
    else return r[e] || undefined
  }, obj)
}

function compare(a, b, prev = "") {
  return Object.keys(a).reduce((r, e) => {
    const path = prev + (prev ? '.' + e : e);
    const value = a[e] === get(b, path);
    r[e] = typeof a[e] === 'object' ? compare(a[e], b, path) : value
    return r;
  }, {})
}

const result = compare(obj1, obj2);
console.log(result)

要比较这两个对象的所有属性,您可以创建额外的功能,以执行两个对象的循环。

const obj1 = {"prop1":1,"prop2":"foo","prop3":{"prop4":2,"prop5":"bar"},"prop7":{"prop9":{"prop10":"foo"}}}
const obj2 = {"prop1":3,"prop2":"foo","prop3":{"prop4":2,"prop5":"foobar"},"prop6":"new","prop7":{"foo":"foo","bar":{"baz":"baz"}}}

function get(obj, path) {
  return path.split('.').reduce((r, e) => {
    if (!r) return r;
    else return r[e] || undefined;
  }, obj);
}

function isEmpty(o) {
  if (typeof o !== 'object') return true;
  else return !Object.keys(o).length;
}

function build(a, b, o = null, prev = '') {
  return Object.keys(a).reduce(
    (r, e) => {
      const path = prev + (prev ? '.' + e : e);
      const bObj = get(b, path);
      const value = a[e] === bObj;

      if (typeof a[e] === 'object') {
        if (isEmpty(a[e]) && isEmpty(bObj)) {
          if (e in r) r[e] = r[e];
          else r[e] = true;
        } else if (!bObj && isEmpty(a[e])) {
          r[e] = value;
        } else {
          r[e] = build(a[e], b, r[e], path);
        }
      } else {
        r[e] = value;
      }
      return r;
    },
    o ? o : {}
  );
}

function compare(a, b) {
  const o = build(a, b);
  return build(b, a, o);
}

const result = compare(obj1, obj2);
console.log(result)

答案 3 :(得分:1)

一个递归示例,

var obj1 = {
        prop1: 1,
        prop2: "foo",
        prop3: {
            prop4: 2,
            prop5: "bar"
        },
        prop7: {},
    }

    var obj2 = {
        prop1: 3,
        prop2: "foo",
        prop3: {
            prop4: 2,
            prop5: "foobar"
        },
        prop6: "new",
        prop7: {},
        prop8: {},
    }

    var result = {};

    function compare(obj1, obj2, obj_) {
        for (let k in obj1) {
            var type = typeof obj1[k];
            if (type === 'object') {
                obj_[k] = {};
                if (!obj2[k]){
                    obj_[k] = false;
                }else if ((Object.entries(obj1[k]).length === 0 && obj1[k].constructor === Object) && (Object.entries(obj2[k]).length === 0 && obj2[k].constructor === Object)) {
                    obj_[k] = true;
                } else {
                    compare(obj1[k], obj2[k], obj_[k]);
                }
            } else {
                obj_[k] = (obj1[k] === obj2[k]);
            }

        }
    }

    if (Object.keys(obj1).length < Object.keys(obj2).length) { //check if both objects vary in length.
        var tmp = obj1;
        obj1 = obj2;
        obj2 = tmp;
    }

    compare(obj1, obj2, result);

    console.log(result);

答案 4 :(得分:1)

这是我最近提出的可以解决相同问题的解决方案,它需要一个可选键来设置比较严格性。在后端将值作为数字发送但希望该值作为字符串返回时很有用。我们一直在使用JSON.stringify比较,但是它有点粗糙,无法说明对象何时相同但键的顺序不同。

public objectsAreTheSame(objA: {[key: string]: any}, objB: {[key: string]: any}, isStrict = false): boolean {
        let areTheSame = true;

        const strictLevel = isStrict ? 'isStrict' : 'isNotStrict';

        const valuesDoNotMatch = {
            'isStrict': (a, b) => a !== b,
            'isNotStrict': (a, b) => a != b
        };

        const isObject = (a, b) => typeof a === 'object' && !Array.isArray(a) && (!!a && !!b);

        const compareArrays = (a, b) => {
            if (a.length === b.length) {
                a.sort();
                b.sort();

                a.forEach((ele, idx) => compareValues(ele, b[idx]));
            } else {
                areTheSame = false;
            }
        };

        const compareValues = (a, b) => {
            if (Array.isArray(a)) {
                compareArrays(a, b);
            } else if (!isObject(a, b) && valuesDoNotMatch[strictLevel](a, b)) {
                areTheSame = false;
            } else if (isObject(a, b) && !this.objectsAreTheSame(a, b, isStrict)) {
                areTheSame = false;
            }
        };

        const keysA = Object.keys(objA);
        const keysB = Object.keys(objB);

        if (keysA.length !== keysB.length) return false;

        for (let key of keysA) compareValues(objA[key], objB[key]);

        return areTheSame;
    }