我有两个对象:oldObj和newObj。

oldObj中的数据用于填充表单,而newObj是用户更改该表单中的数据并提交的结果。

这两个物体都很深。它们具有对象或对象数组等属性-它们可以有n层深,因此diff算法需要递归。

现在我不仅需要弄清楚从oldObj到newObj更改了什么(如添加/更新/删除),而且还需要知道如何最好地表示它。

到目前为止,我的想法只是构建一个通用的deepdiffbetweenobjects方法,该方法将返回窗体上的对象{add:{…},upd:{…},del:{…但我转念一想:以前一定有人需要它。

所以…有没有人知道一个库或一段代码可以做到这一点,并且可能有更好的方式来表示差异(以一种仍然是JSON可序列化的方式)?

更新:

我想到了一个更好的方法来表示更新的数据,通过使用与newObj相同的对象结构,但将所有属性值转换为窗体上的对象:

{type: '<update|create|delete>', data: <propertyValue>}

如果newObj。prop1 = 'new value'和oldObj。prop1 = 'old value'它将设置returnbj。Prop1 = {type: 'update', data: 'new value'}

更新2:

当我们处理数组的属性时,它会变得非常麻烦,因为数组[1,2,3]应该被计算为等于[2,3,1],这对于基于值的类型的数组(如string, int和bool)来说足够简单,但是当涉及到引用类型的数组(如对象和数组)时就很难处理了。

数组的示例应该是相等的:

[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]

不仅要检查这种类型的深度值相等相当复杂,而且要找出一种表示可能发生的变化的好方法。


当前回答

这将把[1,2,3]和[3,2,1]视为相等(deep object) 因为我需要把它们之间的区别形象化:

[
  {
    "a":1,
    "b":1
  },
  {
    "a":1,
    "b":1
  }
]

and

[
  {
    "a":1,
    "b":1
  },
  {
    "a":"OH NO",
    "b":"an insertion"
  },
  {
    "a":1,
    "b":1
  }
]

所以我想看看它们的碰撞,这是剩下的:

[]
and
[
  {
    "a":"OH NO",
    "b":"an insertion"
  }
]

在我看来,这是最好的表现方式。 {添加:{…},乌利希期刊指南:{…},德尔:{…}}很难读懂


我提供了2个函数:objectcollision (obj1,obj2)和arraycollision (arr1,arr2)

console.log(ArrayCollide([1,2,3],[3,2,1]))
// false
//everything collided -> false
console.log(ArrayCollide([1],[2,1]))
// [ [], [ 2 ] ]
//1 and 1 collided, even if they are on different indices

//array of objects
const arr1 = 
[
  {
    "a":1,
    "b":1
  },
  {
    "a":1,
    "b":1
  }
]
const arr2 = 
[
  {
    "a":1,
    "b":1
  },
  {
    "a":"OH NO",
    "b":"an insertion"
  },
  {
    "a":1,
    "b":1
  }
]
const newArrays = ArrayCollide(arr1, arr2)
console.log(newArrays[0])
console.log(newArrays[1])
console.log('\n')
// []
// [ { a: 'OH NO', b: 'an insertion' } ]
// everything collided until this is left

//ObjectCollide
const obj1 = { a: '111', c: { q: 'no', a: '333' } }
const obj2 = { a: '111', p: 'ok', c: { a: '333' } }
ObjectCollide(obj1, obj2) //in place
console.log(obj1)
console.log(obj2)
console.log('\n')
// { c: { q: 'no' } }
// { p: 'ok', c: {} }
// obj["a"] collided and obj["c"]["a"] collided

//testing empty array
const a1 = { a: [] }
const a2 = { a: [], b: '2' }
ObjectCollide(a1, a2) //in place
console.log(a1)
console.log(a2)
console.log('\n')
// {}
// { b: '2' }
// obj["a"] collided

//DIFFERENT TYPES
const b1 = {a:true}
const b2 = {a:[1,2]}
ObjectCollide(b1,b2) //in place
console.log(b1)
console.log(b2)
// { a: true }
// { a: [ 1, 2 ] }

function ObjectCollide(obj1, obj2) {
  //in place, returns true if same

  // delete same
  const keys = Object.keys(obj1)
  const len = keys.length
  let howManyDeleted = 0
  for (let i = 0; i < len; i++) {
    const key = keys[i]

    const type1 = Array.isArray(obj1[key]) === true ? 'array' : typeof obj1[key]
    const type2 = Array.isArray(obj2[key]) === true ? 'array' : typeof obj2[key]
    if (type1!==type2) {
      continue
    }
    switch (type1) {
      case 'object':
        if (ObjectCollide(obj1[key], obj2[key])) {
          delete obj1[key]
          delete obj2[key]
          howManyDeleted++
        }
        continue
      case 'array':
        const newArrays = ArrayCollide(obj1[key], obj2[key])
        if (newArrays) {
          obj1[key] = newArrays[0]
          obj2[key] = newArrays[1]
        } else {
          delete obj1[key]
          delete obj2[key]
          howManyDeleted++
        }
        continue
      default:
        //string, number, I hope it covers everything else
        if (obj1[key] === obj2[key]) {
          delete obj1[key]
          delete obj2[key]
          howManyDeleted++
        }
    }
  }


  if (howManyDeleted === len && Object.keys(obj2).length === 0) {
    // return 'delete the stuff'
    // same. we've deleted everything!
    return true
  }

}
function ArrayCollide(arr1, arr2) {
  // returns [newArr1, newArr2] or false if same arrays (ignore order)
  const stringifyObj = {}

  const newArr1 = []
  const newArr2 = []
  for (let i = 0, len = arr1.length; i < len; i++) {
    const value = arr1[i]
    const stringified = JSON.stringify(value)
    stringifyObj[stringified]
    // arr = [count, ...]
    const arr = stringifyObj[stringified] || (stringifyObj[stringified] = [0])
    arr[0]++
    arr.push(value)
  }
  //in 2 but not in 1
  for (let i = 0, len = arr2.length; i < len; i++) {
    const value = arr2[i]
    const stringified = JSON.stringify(value)
    const arr = stringifyObj[stringified]
    if (arr === undefined) {
      newArr2.push(value)
    } else {
      if (arr[0] === 0) {
        newArr2.push(value)
      } else {
        arr[0]--
      }
    }
  }
  //in 1 but not in 2
  stringifyKeys = Object.keys(stringifyObj)
  for (let i = 0, len = stringifyKeys.length; i < len; i++) {
    const arr = stringifyObj[stringifyKeys[i]]

    for (let i = 1, len = arr[0] + 1; i < len; i++) {
      newArr1.push(arr[i])
    }
  }
  if (newArr1.length || newArr2.length) {
    return [newArr1, newArr2]
  } else {
    return false
  }

}

我想解决的问题是:

JSON文件不断重新排序,我想恢复JSON,如果它是等效的:像{a:1,b:2}和{b:2,a:1} 但是因为我不相信我的代码(我犯了一个错误),我想看到diff并自己检查它,我可以Ctrl+F到原始文件使用这个diff。

其他回答

var base = [
{"value": "01", "label": "Pendências"},
{"value": "02", "label": "Ambulatório"},
{"value": "03", "label": "Urgência"},
{"value": "04", "label": "Clínica Médica"},
{"value": "05", "label": "Revisão"},
{"value": "06", "label": "Imagens"},
];

var used = [
{"value": "01", "label": "Pendências"},
{"value": "02", "label": "Ambulatório"},
{"value": "03", "label": "Urgência"},
{"value": "04", "label": "Clínica Médica"},
];

function diff(obj1,obj2) {
        var temp = JSON.stringify(obj2.map((x)=> x.value));
    return obj1.filter((y)=> temp.indexOf(y.value)<0 && y);
}


var result = diff(base, used); 
console.clear();
console.log('RESULTADO');
console.log(result);

codeped

下面的方法将创建一个只更改字段的新对象

const findDiff = (obj1, obj2) => {
  const isNativeType1 = typeof obj1 !== "object";
  const isNativeType2 = typeof obj2 !== "object";
  if (isNativeType1 && isNativeType2) {
    return obj1 === obj2 ? null : obj2;
  }
  if (isNativeType1 && !isNativeType2) {
    return obj2;
  }
  if (!isNativeType1 && isNativeType2) {
    return obj2;
  }
  const isArray1 = Array.isArray(obj1);
  const isArray2 = Array.isArray(obj2);
  if (isArray1 && isArray2) {
    const firstLenght = obj1.length;
    const secondLenght = obj2.length;
    const hasSameLength = firstLenght === secondLenght;
    if (!hasSameLength) return obj2;
    let hasChange = false;
    for (let index = 0; index < obj1.length; index += 1) {
      const element1 = obj1[index];
      const element2 = obj2[index];
      const changed = findDiff(element1, element2);
      if (changed) {
        hasChange = true;
      }
    }
    return hasChange ? obj2 : null;
  }
  if (isArray1 || isArray2) return obj2;
  const keys1 = Object.keys(obj1);
  const keys2 = Object.keys(obj2);
  const hasSameKeys = keys1.length === keys2.length;
  if (!hasSameKeys) {
    const retObj = { ...obj2 };
    for (let index = 0; index < keys1.length; index += 1) {
      const key = keys1[index];
      if (!keys2.includes(key)) {
        retObj[key] = null;
        // eslint-disable-next-line no-continue
        continue;
      }
      delete retObj[key];
    }
    return retObj;
  }
  let hasChange = false;
  const retObj = {};
  for (let index = 0; index < keys1.length; index += 1) {
    const key = keys1[index];
    const element1 = obj1[key];
    const element2 = obj2[key];
    const changed = findDiff(element1, element2);
    if (changed) {
      hasChange = true;
    }
    if (changed) {
      retObj[key] = changed;
    }
  }
  return hasChange ? retObj : null;
};

console.log(
  JSON.stringify(findDiff(
    {
      a: 1,
      b: 2,
      c: {
        a: ['1', 'b', { a: 'b', c: false }, true],
      },
    },
    {
      a: 1,
      b: 2,
      c: {
        a: ['1','b', { a: 'b', c: true }, true],
      },
    }
  ), null, 2)
);

这将把[1,2,3]和[3,2,1]视为相等(deep object) 因为我需要把它们之间的区别形象化:

[
  {
    "a":1,
    "b":1
  },
  {
    "a":1,
    "b":1
  }
]

and

[
  {
    "a":1,
    "b":1
  },
  {
    "a":"OH NO",
    "b":"an insertion"
  },
  {
    "a":1,
    "b":1
  }
]

所以我想看看它们的碰撞,这是剩下的:

[]
and
[
  {
    "a":"OH NO",
    "b":"an insertion"
  }
]

在我看来,这是最好的表现方式。 {添加:{…},乌利希期刊指南:{…},德尔:{…}}很难读懂


我提供了2个函数:objectcollision (obj1,obj2)和arraycollision (arr1,arr2)

console.log(ArrayCollide([1,2,3],[3,2,1]))
// false
//everything collided -> false
console.log(ArrayCollide([1],[2,1]))
// [ [], [ 2 ] ]
//1 and 1 collided, even if they are on different indices

//array of objects
const arr1 = 
[
  {
    "a":1,
    "b":1
  },
  {
    "a":1,
    "b":1
  }
]
const arr2 = 
[
  {
    "a":1,
    "b":1
  },
  {
    "a":"OH NO",
    "b":"an insertion"
  },
  {
    "a":1,
    "b":1
  }
]
const newArrays = ArrayCollide(arr1, arr2)
console.log(newArrays[0])
console.log(newArrays[1])
console.log('\n')
// []
// [ { a: 'OH NO', b: 'an insertion' } ]
// everything collided until this is left

//ObjectCollide
const obj1 = { a: '111', c: { q: 'no', a: '333' } }
const obj2 = { a: '111', p: 'ok', c: { a: '333' } }
ObjectCollide(obj1, obj2) //in place
console.log(obj1)
console.log(obj2)
console.log('\n')
// { c: { q: 'no' } }
// { p: 'ok', c: {} }
// obj["a"] collided and obj["c"]["a"] collided

//testing empty array
const a1 = { a: [] }
const a2 = { a: [], b: '2' }
ObjectCollide(a1, a2) //in place
console.log(a1)
console.log(a2)
console.log('\n')
// {}
// { b: '2' }
// obj["a"] collided

//DIFFERENT TYPES
const b1 = {a:true}
const b2 = {a:[1,2]}
ObjectCollide(b1,b2) //in place
console.log(b1)
console.log(b2)
// { a: true }
// { a: [ 1, 2 ] }

function ObjectCollide(obj1, obj2) {
  //in place, returns true if same

  // delete same
  const keys = Object.keys(obj1)
  const len = keys.length
  let howManyDeleted = 0
  for (let i = 0; i < len; i++) {
    const key = keys[i]

    const type1 = Array.isArray(obj1[key]) === true ? 'array' : typeof obj1[key]
    const type2 = Array.isArray(obj2[key]) === true ? 'array' : typeof obj2[key]
    if (type1!==type2) {
      continue
    }
    switch (type1) {
      case 'object':
        if (ObjectCollide(obj1[key], obj2[key])) {
          delete obj1[key]
          delete obj2[key]
          howManyDeleted++
        }
        continue
      case 'array':
        const newArrays = ArrayCollide(obj1[key], obj2[key])
        if (newArrays) {
          obj1[key] = newArrays[0]
          obj2[key] = newArrays[1]
        } else {
          delete obj1[key]
          delete obj2[key]
          howManyDeleted++
        }
        continue
      default:
        //string, number, I hope it covers everything else
        if (obj1[key] === obj2[key]) {
          delete obj1[key]
          delete obj2[key]
          howManyDeleted++
        }
    }
  }


  if (howManyDeleted === len && Object.keys(obj2).length === 0) {
    // return 'delete the stuff'
    // same. we've deleted everything!
    return true
  }

}
function ArrayCollide(arr1, arr2) {
  // returns [newArr1, newArr2] or false if same arrays (ignore order)
  const stringifyObj = {}

  const newArr1 = []
  const newArr2 = []
  for (let i = 0, len = arr1.length; i < len; i++) {
    const value = arr1[i]
    const stringified = JSON.stringify(value)
    stringifyObj[stringified]
    // arr = [count, ...]
    const arr = stringifyObj[stringified] || (stringifyObj[stringified] = [0])
    arr[0]++
    arr.push(value)
  }
  //in 2 but not in 1
  for (let i = 0, len = arr2.length; i < len; i++) {
    const value = arr2[i]
    const stringified = JSON.stringify(value)
    const arr = stringifyObj[stringified]
    if (arr === undefined) {
      newArr2.push(value)
    } else {
      if (arr[0] === 0) {
        newArr2.push(value)
      } else {
        arr[0]--
      }
    }
  }
  //in 1 but not in 2
  stringifyKeys = Object.keys(stringifyObj)
  for (let i = 0, len = stringifyKeys.length; i < len; i++) {
    const arr = stringifyObj[stringifyKeys[i]]

    for (let i = 1, len = arr[0] + 1; i < len; i++) {
      newArr1.push(arr[i])
    }
  }
  if (newArr1.length || newArr2.length) {
    return [newArr1, newArr2]
  } else {
    return false
  }

}

我想解决的问题是:

JSON文件不断重新排序,我想恢复JSON,如果它是等效的:像{a:1,b:2}和{b:2,a:1} 但是因为我不相信我的代码(我犯了一个错误),我想看到diff并自己检查它,我可以Ctrl+F到原始文件使用这个diff。

下面是@sbgoran代码的typescript版本

export class deepDiffMapper {

  static VALUE_CREATED = 'created';
  static VALUE_UPDATED = 'updated';
  static VALUE_DELETED = 'deleted';
  static VALUE_UNCHANGED ='unchanged';

  protected isFunction(obj: object) {
    return {}.toString.apply(obj) === '[object Function]';
  };

  protected isArray(obj: object) {
      return {}.toString.apply(obj) === '[object Array]';
  };

  protected isObject(obj: object) {
      return {}.toString.apply(obj) === '[object Object]';
  };

  protected isDate(obj: object) {
      return {}.toString.apply(obj) === '[object Date]';
  };

  protected isValue(obj: object) {
      return !this.isObject(obj) && !this.isArray(obj);
  };

  protected compareValues (value1: any, value2: any) {
    if (value1 === value2) {
        return deepDiffMapper.VALUE_UNCHANGED;
    }
    if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
        return deepDiffMapper.VALUE_UNCHANGED;
    }
    if ('undefined' == typeof(value1)) {
        return deepDiffMapper.VALUE_CREATED;
    }
    if ('undefined' == typeof(value2)) {
        return deepDiffMapper.VALUE_DELETED;
    }

    return deepDiffMapper.VALUE_UPDATED;
  }

  public map(obj1: object, obj2: object) {
      if (this.isFunction(obj1) || this.isFunction(obj2)) {
          throw 'Invalid argument. Function given, object expected.';
      }
      if (this.isValue(obj1) || this.isValue(obj2)) {
          return {
              type: this.compareValues(obj1, obj2),
              data: (obj1 === undefined) ? obj2 : obj1
          };
      }

      var diff = {};
      for (var key in obj1) {
          if (this.isFunction(obj1[key])) {
              continue;
          }

          var value2 = undefined;
          if ('undefined' != typeof(obj2[key])) {
              value2 = obj2[key];
          }

          diff[key] = this.map(obj1[key], value2);
      }
      for (var key in obj2) {
          if (this.isFunction(obj2[key]) || ('undefined' != typeof(diff[key]))) {
              continue;
          }

          diff[key] = this.map(undefined, obj2[key]);
      }

      return diff;

  }
}

我只是使用ramda,为了解决同样的问题,我需要知道在新对象中有什么改变。这是我的设计。

const oldState = {id:'170',name:'Ivab',secondName:'Ivanov',weight:45};
const newState = {id:'170',name:'Ivanko',secondName:'Ivanov',age:29};

const keysObj1 = R.keys(newState)

const filterFunc = key => {
  const value = R.eqProps(key,oldState,newState)
  return {[key]:value}
}

const result = R.map(filterFunc, keysObj1)

结果是属性的名称和状态。

[{"id":true}, {"name":false}, {"secondName":true}, {"age":false}]