Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/410.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何使用lodash对两个对象进行深入比较?_Javascript_Lodash - Fatal编程技术网

Javascript 如何使用lodash对两个对象进行深入比较?

Javascript 如何使用lodash对两个对象进行深入比较?,javascript,lodash,Javascript,Lodash,我有两个不同的嵌套对象,我需要知道它们的一个嵌套属性是否有差异 var a={}; var b={}; a、 prop1=2; a、 prop2={prop3:2}; b、 prop1=2; b、 prop2={prop3:3}; 对象可能更复杂,具有更多嵌套属性。但这是一个很好的例子。我可以选择使用递归函数或lodash…一个简单而优雅的解决方案,它可以进行深入的比较: _.reduce(a, function(result, value, key) { return b[key

我有两个不同的嵌套对象,我需要知道它们的一个嵌套属性是否有差异

var a={};
var b={};
a、 prop1=2;
a、 prop2={prop3:2};
b、 prop1=2;
b、 prop2={prop3:3};

对象可能更复杂,具有更多嵌套属性。但这是一个很好的例子。我可以选择使用递归函数或lodash…

一个简单而优雅的解决方案,它可以进行深入的比较:

 _.reduce(a, function(result, value, key) {
     return b[key] === undefined ? key : []
  }, []);
var a={};
var b={};
a、 prop1=2;
a、 prop2={prop3:2};
b、 prop1=2;
b、 prop2={prop3:3};
console.log(u.isEqual(a,b));//如果不同

如果需要知道哪些属性不同,请使用:


以下是一个简洁的解决方案:


注:两个输入都需要是数组(可能是一个对象的数组)。

完成Adam Boduch的回答后,这个输入考虑了属性的差异

const differenceOfKeys = (...objects) =>
  _.difference(...objects.map(obj => Object.keys(obj)));
const differenceObj = (a, b) => 
  _.reduce(a, (result, value, key) => (
    _.isEqual(value, b[key]) ? result : [...result, key]
  ), differenceOfKeys(b, a));

这段代码返回一个对象,该对象包含具有不同值的所有属性以及两个对象的值。用于记录差异

var allkeys = _.union(_.keys(obj1), _.keys(obj2));
var difference = _.reduce(allkeys, function (result, key) {
  if ( !_.isEqual(obj1[key], obj2[key]) ) {
    result[key] = {obj1: obj1[key], obj2: obj2[key]}
  }
  return result;
}, {});

如果只需要键比较:

 _.reduce(a, function(result, value, key) {
     return b[key] === undefined ? key : []
  }, []);

对于任何遇到这个问题的人,这里有一个更完整的解决方案。它将比较两个对象,并为您提供所有属性的键,这些属性要么仅在object1中,要么仅在object2中,要么同时在object1和object2中,但值不同:

/*
 * Compare two objects by reducing an array of keys in obj1, having the
 * keys in obj2 as the intial value of the result. Key points:
 *
 * - All keys of obj2 are initially in the result.
 *
 * - If the loop finds a key (from obj1, remember) not in obj2, it adds
 *   it to the result.
 *
 * - If the loop finds a key that are both in obj1 and obj2, it compares
 *   the value. If it's the same value, the key is removed from the result.
 */
function getObjectDiff(obj1, obj2) {
    const diff = Object.keys(obj1).reduce((result, key) => {
        if (!obj2.hasOwnProperty(key)) {
            result.push(key);
        } else if (_.isEqual(obj1[key], obj2[key])) {
            const resultKeyIndex = result.indexOf(key);
            result.splice(resultKeyIndex, 1);
        }
        return result;
    }, Object.keys(obj2));

    return diff;
}
以下是一个示例输出:

// Test
let obj1 = {
    a: 1,
    b: 2,
    c: { foo: 1, bar: 2},
    d: { baz: 1, bat: 2 }
}

let obj2 = {
    b: 2, 
    c: { foo: 1, bar: 'monkey'}, 
    d: { baz: 1, bat: 2 }
    e: 1
}
getObjectDiff(obj1, obj2)
// ["c", "e", "a"]
如果您不关心嵌套对象并希望跳过lodash,则可以用
.isEqual
替换正常值比较,例如
obj1[key]==obj2[key]

基于此,我编写了这个函数以尽可能深的意义比较两个对象,返回具有不同值的路径以及一个或另一个对象缺少的路径

代码的编写并没有考虑到效率,这方面的改进是最受欢迎的,但以下是基本形式:

var compare = function (a, b) {

  var result = {
    different: [],
    missing_from_first: [],
    missing_from_second: []
  };

  _.reduce(a, function (result, value, key) {
    if (b.hasOwnProperty(key)) {
      if (_.isEqual(value, b[key])) {
        return result;
      } else {
        if (typeof (a[key]) != typeof ({}) || typeof (b[key]) != typeof ({})) {
          //dead end.
          result.different.push(key);
          return result;
        } else {
          var deeper = compare(a[key], b[key]);
          result.different = result.different.concat(_.map(deeper.different, (sub_path) => {
            return key + "." + sub_path;
          }));

          result.missing_from_second = result.missing_from_second.concat(_.map(deeper.missing_from_second, (sub_path) => {
            return key + "." + sub_path;
          }));

          result.missing_from_first = result.missing_from_first.concat(_.map(deeper.missing_from_first, (sub_path) => {
            return key + "." + sub_path;
          }));
          return result;
        }
      }
    } else {
      result.missing_from_second.push(key);
      return result;
    }
  }, result);

  _.reduce(b, function (result, value, key) {
    if (a.hasOwnProperty(key)) {
      return result;
    } else {
      result.missing_from_first.push(key);
      return result;
    }
  }, result);

  return result;
}
您可以使用此代码段尝试代码(建议以整页模式运行):

var比较=函数(a,b){
var结果={
不同:[],
_first中缺少_:[],
_秒中缺少_:[]
};
_.减少(a、函数(结果、值、键){
如果(b.hasOwnProperty(键)){
if(u.isEqual(值,b[键]){
返回结果;
}否则{
if(typeof(a[key])!=typeof({})| | typeof(b[key])!=typeof({})){
//死胡同。
结果。不同。按(键);
返回结果;
}否则{
var=比较(a[键]、b[键]);
result.different=result.different.concat(u.map(deep.different,(sub_路径)=>{
返回键+“+”子路径;
}));
result.missing_from_second=result.missing_from_second.concat(u.map(deep.missing_from_second,(sub_路径)=>{
返回键+“+”子路径;
}));
result.missing_from_first=result.missing_from_first.concat(u.map(deep.missing_from_first,(子路径)=>{
返回键+“+”子路径;
}));
返回结果;
}
}
}否则{
结果。第二次按(键)时缺少\u;
返回结果;
}
},结果);
_.reduce(b,函数(结果、值、键){
如果(a.hasOwnProperty(键)){
返回结果;
}否则{
结果。第一个\u中缺少\u。按(键);
返回结果;
}
},结果);
返回结果;
}
var a#u editor=new JSONEditor($('#a')[0]{
名称:‘a’,
模式:“代码”
});
var b#u editor=new JSONEditor($('#b')[0]{
名称:‘b’,
模式:“代码”
});
变量a={
相同:1,,
不同:二,,
_b:3中缺少_,
_b中缺少_嵌套的_:{
x:1,
y:2
},
嵌套的:{
相同:1,,
不同:二,,
_b中缺少_:3
}
}
变量b={
相同:1,,
不同:99,
_a:3中缺少_,
_a中缺少_嵌套的_:{
x:1,
y:2
},
嵌套的:{
相同:1,,
不同:99,
_a中缺少_:3
}
}
a_编辑器集(a);
b_编辑器集(b);
var result_editor=new JSONEditor($('#result')[0]{
名称:“结果”,
模式:“查看”
});
var do_compare=函数(){
var a=a_editor.get();
var b=b_editor.get();
结果编辑器集合(比较(a,b));
}
#对象{}#对象部分{
边缘底部:10px;
}
#对象部分h1{
背景:#444;
颜色:白色;
字体系列:monospace;
显示:内联块;
保证金:0;
填充物:5px;
}
.jsoneditor外部.ace\u编辑器{
最小高度:230px!重要;
}
按钮:悬停{
背景:橙色;
}
钮扣{
光标:指针;
背景:红色;
颜色:白色;
文本对齐:左对齐;
字体大小:粗体;
边框:5px深红色;
大纲:0;
填充:10px;
利润率:10px 0px;
}

a(第一个对象)
b(第二个对象)
比较
结果

我尝试了Adam Boduch的代码来输出一个深度差异-这完全没有经过测试,但有以下几点:

function diff (obj1, obj2, path) {
    obj1 = obj1 || {};
    obj2 = obj2 || {};

    return _.reduce(obj1, function(result, value, key) {
        var p = path ? path + '.' + key : key;
        if (_.isObject(value)) {
            var d = diff(value, obj2[key], p);
            return d.length ? result.concat(d) : result;
        }
        return _.isEqual(value, obj2[key]) ? result : result.concat(p);
    }, []);
}

diff({ foo: 'lol', bar: { baz: true }}, {}) // returns ["foo", "bar.baz"]

在不使用lodash/下划线的情况下,我已经编写了这段代码,并且在深入比较object1和object2方面做得很好

function getObjectDiff(a, b) {
    var diffObj = {};
    if (Array.isArray(a)) {
        a.forEach(function(elem, index) {
            if (!Array.isArray(diffObj)) {
                diffObj = [];
            }
            diffObj[index] = getObjectDiff(elem, (b || [])[index]);
        });
    } else if (a != null && typeof a == 'object') {
        Object.keys(a).forEach(function(key) {
            if (Array.isArray(a[key])) {
                var arr = getObjectDiff(a[key], b[key]);
                if (!Array.isArray(arr)) {
                    arr = [];
                }
                arr.forEach(function(elem, index) {
                    if (!Array.isArray(diffObj[key])) {
                        diffObj[key] = [];
                    }
                    diffObj[key][index] = elem;
                });
            } else if (typeof a[key] == 'object') {
                diffObj[key] = getObjectDiff(a[key], b[key]);
            } else if (a[key] != (b || {})[key]) {
                diffObj[key] = a[key];
            } else if (a[key] == (b || {})[key]) {
                delete a[key];
            }
        });
    }
    Object.keys(diffObj).forEach(function(key) {
        if (typeof diffObj[key] == 'object' && JSON.stringify(diffObj[key]) == '{}') {
            delete diffObj[key];
        }
    });
    return diffObj;
}

使用(嵌套)属性模板进行深度比较以进行检查

function objetcsDeepEqualByTemplate(objectA, objectB, comparisonTemplate) {
  if (!objectA || !objectB) return false

  let areDifferent = false
  Object.keys(comparisonTemplate).some((key) => {
    if (typeof comparisonTemplate[key] === 'object') {
      areDifferent = !objetcsDeepEqualByTemplate(objectA[key], objectB[key], comparisonTemplate[key])
      return areDifferent
    } else if (comparisonTemplate[key] === true) {
      areDifferent = objectA[key] !== objectB[key]
      return areDifferent
    } else {
      return false
    }
  })

  return !areDifferent
}

const objA = { 
  a: 1,
  b: {
    a: 21,
    b: 22,
  },
  c: 3,
}

const objB = { 
  a: 1,
  b: {
    a: 21,
    b: 25,
  },
  c: true,
}

// template tells which props to compare
const comparisonTemplateA = {
  a: true,
  b: {
    a: true
  }
}
objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateA)
// returns true

const comparisonTemplateB = {
  a: true,
  c: true
}
// returns false
objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateB)

这将在控制台中工作。如果需要,可以添加数组支持

,以递归方式显示对象与其他对象之间的差异,您可以使用u.reduce与u.isEqual和u.isPlainObject组合使用。在这种情况下,您可以比较a与b之间的差异,或者b与a之间的差异:

const objectA={
a:{
1:“结果中将丢失相同内容”,
2:“BBB”,
3: [1, 2, 3]
}, 
b:“不是”,
c:“富吧”
};
常量对象B={
a:{
1:“结果中将丢失相同内容”,
2: [1, 2]
}, 
b:“福”,
c:“酒吧”
};
常数差=功能(obj1、obj2){
返回还原(obj1,函数(结果、值、键){
如果(uu.isPlainObject(值)){
结果[键]=差异(值,obj2[ke
var isEqual = function(f,s) {
  if (f === s) return true;

  if (Array.isArray(f)&&Array.isArray(s)) {
    return isEqual(f.sort(), s.sort());
  }
  if (_.isObject(f)) {
    return isEqual(f, s);
  }
  return _.isEqual(f, s);
};
function objetcsDeepEqualByTemplate(objectA, objectB, comparisonTemplate) {
  if (!objectA || !objectB) return false

  let areDifferent = false
  Object.keys(comparisonTemplate).some((key) => {
    if (typeof comparisonTemplate[key] === 'object') {
      areDifferent = !objetcsDeepEqualByTemplate(objectA[key], objectB[key], comparisonTemplate[key])
      return areDifferent
    } else if (comparisonTemplate[key] === true) {
      areDifferent = objectA[key] !== objectB[key]
      return areDifferent
    } else {
      return false
    }
  })

  return !areDifferent
}

const objA = { 
  a: 1,
  b: {
    a: 21,
    b: 22,
  },
  c: 3,
}

const objB = { 
  a: 1,
  b: {
    a: 21,
    b: 25,
  },
  c: true,
}

// template tells which props to compare
const comparisonTemplateA = {
  a: true,
  b: {
    a: true
  }
}
objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateA)
// returns true

const comparisonTemplateB = {
  a: true,
  c: true
}
// returns false
objetcsDeepEqualByTemplate(objA, objB, comparisonTemplateB)
let differences = function (newObj, oldObj) {
      return _.reduce(newObj, function (result, value, key) {
        if (!_.isEqual(value, oldObj[key])) {
          if (_.isArray(value)) {
            result[key] = []
            _.forEach(value, function (innerObjFrom1, index) {
              if (_.isNil(oldObj[key][index])) {
                result[key].push(innerObjFrom1)
              } else {
                let changes = differences(innerObjFrom1, oldObj[key][index])
                if (!_.isEmpty(changes)) {
                  result[key].push(changes)
                }
              }
            })
          } else if (_.isObject(value)) {
            result[key] = differences(value, oldObj[key])
          } else {
            result[key] = value
          }
        }
        return result
      }, {})
    }
var bdiff = (a, b) =>
    _.reduce(a, (res, val, key) =>
        res.concat((_.isPlainObject(val) || _.isArray(val)) && b
            ? bdiff(val, b[key]).map(x => key + '.' + x) 
            : (!b || val != b[key] ? [key] : [])),
        []);
var diff = bdiff(expected, actual);
// all expected properties match
console.assert(diff.length == 0, "Objects differ", diff, expected, actual);
// controlled inequality
console.assert(diff.length < 3, "Too many differences", diff, expected, actual);
function diff(a, b) {
    var u = bdiff(a, b), v = bdiff(b, a);
    return u.filter(x=>!v.includes(x)).map(x=>' < ' + x)
    .concat(u.filter(x=>v.includes(x)).map(x=>' | ' + x))
    .concat(v.filter(x=>!u.includes(x)).map(x=>' > ' + x));
};
 [
  " < components.0.components.1.components.1.isNew",
  " < components.0.cryptoKey",
  " | components.0.components.2.components.2.components.2.FFT.min",
  " | components.0.components.2.components.2.components.2.FFT.max",
  " > components.0.components.1.components.1.merkleTree",
  " > components.0.components.2.components.2.components.2.merkleTree",
  " > components.0.components.3.FFTResult"
 ]
// provides syntactically correct output
var bdiff = (a, b) =>
    _.reduce(a, (res, val, key) =>
        res.concat((_.isPlainObject(val) || _.isArray(val)) && b
            ? bdiff(val, b[key]).map(x => 
                key + (key.trim ? '':']') + (x.search(/^\d/)? '.':'[') + x)
            : (!b || val != b[key] ? [key + (key.trim ? '':']')] : [])),
        []);

// now we can eval output of the diff fuction that we left unchanged
diff(a, b).filter(x=>x[1] == '|').map(x=>[x].concat([a, b].map(y=>((z) =>eval('z.' + x.substr(3))).call(this, y)))));
[" | components[0].components[2].components[2].components[2].FFT.min", 0, 3]
[" | components[0].components[2].components[2].components[2].FFT.max", 100, 50]
const oldData = {a: 1, b: 2};
const newData = {a: 1, b: 3};
const result: {b: 3};
import * as _ from "lodash";

export const objectDeepDiff = (data: object | any, oldData: object | any) => {
  const record: any = {};
  Object.keys(data).forEach((key: string) => {
    // Checks that isn't an object and isn't equal
    if (!(typeof data[key] === "object" && _.isEqual(data[key], oldData[key]))) {
      record[key] = data[key];
    }
    // If is an object, and the object isn't equal
    if ((typeof data[key] === "object" && !_.isEqual(data[key], oldData[key]))) {
      record[key] = objectDeepDiff(data[key], oldData[key]);
    }
  });
  return record;
};
 const firstName = {name: "Alireza"};
 const otherName = {name: "Alireza"};
const _ = require('lodash');

var objects = [{ 'x': 1, 'y': 2, 'z':3, a:{b:1, c:2, d:{n:0}}, p:[1, 2, 3]  }, { 'x': 2, 'y': 1, z:3, a:{b:2, c:2,d:{n:1}}, p:[1,3], m:3  }];

const diffFn=(a,b, path='')=>_.reduce(a, function(result, value, key) {

    if(_.isObjectLike(value)){
      if(_.isEqual(value, b[key])){
        return result;
      }else{

return result.concat(diffFn(value, b[key], path?(`${path}.${key}`):key))
      }
    }else{
return _.isEqual(value, b[key]) ?
        result : result.concat(path?(`${path}.${key}`):key);
    }
    
}, []);

const diffKeys1=diffFn(objects[0], objects[1])
const diffKeys2=diffFn(objects[1], objects[0])
const diffKeys=_.union(diffKeys1, diffKeys2)
const res={};

_.forEach(diffKeys, (key)=>_.assign(res, {[key]:{ old: _.get(objects[0], key), new:_.get(objects[1], key)} }))

res
/*
Returns
{
  x: { old: 1, new: 2 },
  y: { old: 2, new: 1 },
  'a.b': { old: 1, new: 2 },
  'a.d.n': { old: 0, new: 1 },
  'p.1': { old: 2, new: 3 },
  'p.2': { old: 3, new: undefined },
  m: { old: undefined, new: 3 }
}
*/
import DeepDiff from 'deep-diff';
let a = {...} //some object
let b = {...} //some object 
var differences = DeepDiff.diff(a, b);