两个对象之间的通用深度差异

Mar*_*sen 194 javascript compare object

我有两个对象:oldObjnewObj.

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

两个对象都很深,即.它们具有对象或对象数组等属性 - 它们可以是n级深度,因此diff算法需要是递归的.

现在我需要不只是从弄清楚什么改变(如添加/更新/删除)oldObjnewObj,却怎么也最能代表它.

到目前为止,我的想法只是构建一个genericDeepDiffBetweenObjects方法,它将返回表单上的对象,{add:{...},upd:{...},del:{...}}但后来我想:其他人必须先需要它.

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

更新:

我想到了一种更好的方式来表示更新的数据,使用相同的对象结构newObj,但将所有属性值转换为表单上的对象:

{type: '<update|create|delete>', data: <propertyValue>}
Run Code Online (Sandbox Code Playgroud)

所以,如果newObj.prop1 = 'new value'oldObj.prop1 = 'old value'它会设置returnObj.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]]
Run Code Online (Sandbox Code Playgroud)

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

sbg*_*ran 125

我写了一个做你想做的小课,你可以在这里测试一下.

只有与你的提议不同的是我不认为[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]是相同的,因为我认为如果元素的顺序不相同,则数组不相等.当然,如果需要,可以更改.此代码还可以进一步增强,以便将函数作为参数,用于根据传递的原始值以任意方式格式化diff对象(现在这个作业由"compareValues"方法完成).

var deepDiffMapper = function () {
  return {
    VALUE_CREATED: 'created',
    VALUE_UPDATED: 'updated',
    VALUE_DELETED: 'deleted',
    VALUE_UNCHANGED: 'unchanged',
    map: function(obj1, obj2) {
      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 (obj2[key] !== undefined) {
          value2 = obj2[key];
        }

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

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

      return diff;

    },
    compareValues: function (value1, value2) {
      if (value1 === value2) {
        return this.VALUE_UNCHANGED;
      }
      if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
        return this.VALUE_UNCHANGED;
      }
      if (value1 === undefined) {
        return this.VALUE_CREATED;
      }
      if (value2 === undefined) {
        return this.VALUE_DELETED;
      }
      return this.VALUE_UPDATED;
    },
    isFunction: function (x) {
      return Object.prototype.toString.call(x) === '[object Function]';
    },
    isArray: function (x) {
      return Object.prototype.toString.call(x) === '[object Array]';
    },
    isDate: function (x) {
      return Object.prototype.toString.call(x) === '[object Date]';
    },
    isObject: function (x) {
      return Object.prototype.toString.call(x) === '[object Object]';
    },
    isValue: function (x) {
      return !this.isObject(x) && !this.isArray(x);
    }
  }
}();


var result = deepDiffMapper.map({
  a: 'i am unchanged',
  b: 'i am deleted',
  e: {
    a: 1,
    b: false,
    c: null
  },
  f: [1, {
    a: 'same',
    b: [{
      a: 'same'
    }, {
      d: 'delete'
    }]
  }],
  g: new Date('2017.11.25')
}, {
  a: 'i am unchanged',
  c: 'i am created',
  e: {
    a: '1',
    b: '',
    d: 'created'
  },
  f: [{
    a: 'same',
    b: [{
      a: 'same'
    }, {
      c: 'create'
    }]
  }, 1],
  g: new Date('2017.11.25')
});
console.log(result);
Run Code Online (Sandbox Code Playgroud)

  • @MartinJespersen好的,你会如何对这个数组进行一般处理:`[{key:'value1'}]和[{key:'value2'},{key:'value3'}]`.现在是第一个使用"value1"或"value2"更新的数组中的第一个对象.这是一个简单的例子,深度嵌套可能会变得非常复杂.如果你想要/需要深度嵌套比较而不管关键位置不创建对象数组,创建具有嵌套对象的对象,如前面的例子:`{inner:{key:'value1'}}和{inner:{key:' value2'},otherInner:{key:'value3'}}`. (6认同)
  • +1这不是一个糟糕的代码.然而有一个bug(请查看这个示例:http://jsfiddle.net/kySNu/3/`c`创建为`undefined`但应该是字符串''我被创建'`),除此之外不做我需要的,因为它缺乏深度数组值比较,这是最重要的(和复杂/困难)部分.作为旁注,构造`'array'!= typeof(obj)`是无用的,因为数组是作为数组实例的对象. (3认同)
  • 当一个答案被大量复制和修改时,你就知道它是好的 (3认同)
  • 我同意你最后的观点 - 原始数据结构应该改为更容易做实际差异的东西.恭喜你,你钉了它:) (2认同)

drz*_*aus 85

使用Underscore,一个简单的差异:

var o1 = {a: 1, b: 2, c: 2},
    o2 = {a: 2, b: 1, c: 2};

_.omit(o1, function(v,k) { return o2[k] === v; })
Run Code Online (Sandbox Code Playgroud)

结果部分o1对应但具有不同的值o2:

{a: 1, b: 2}
Run Code Online (Sandbox Code Playgroud)

深度差异会有所不同:

function diff(a,b) {
    var r = {};
    _.each(a, function(v,k) {
        if(b[k] === v) return;
        // but what if it returns an empty object? still attach?
        r[k] = _.isObject(v)
                ? _.diff(v, b[k])
                : v
            ;
        });
    return r;
}
Run Code Online (Sandbox Code Playgroud)

正如@Juhana在评论中所指出的,上面只是一个差异 - > b而不是可逆的(意味着b中的额外属性将被忽略).请改用 - > b - > a:

(function(_) {
  function deepDiff(a, b, r) {
    _.each(a, function(v, k) {
      // already checked this or equal...
      if (r.hasOwnProperty(k) || b[k] === v) return;
      // but what if it returns an empty object? still attach?
      r[k] = _.isObject(v) ? _.diff(v, b[k]) : v;
    });
  }

  /* the function */
  _.mixin({
    diff: function(a, b) {
      var r = {};
      deepDiff(a, b, r);
      deepDiff(b, a, r);
      return r;
    }
  });
})(_.noConflict());
Run Code Online (Sandbox Code Playgroud)

有关完整示例+ tests + mixins,请参阅http://jsfiddle.net/drzaus/9g5qoxwj/

  • 它应该是 `_.omitBy` 而不是 `_.omit`。 (3认同)
  • @Seiyria讨厌讨厌,我想......我之所以这样做是因为我原本认为`省略`会是一个很大的差异,但是错了,所以也包括在内进行比较. (2认同)
  • 当对象在其他方面相同但第二个对象具有更多元素时,这两个都返回假否定,例如`{a:1, b:2}` 和 `{a:1, b:2, c:3}`。 (2认同)

sen*_*tor 41

我想提供一个ES6解决方案......这是一个单向差异,这意味着它将返回键/值o2与它们中的对应物不同o1:

let o1 = {
  one: 1,
  two: 2,
  three: 3
}

let o2 = {
  two: 2,
  three: 3,
  four: 4
}

let diff = Object.keys(o2).reduce((diff, key) => {
  if (o1[key] === o2[key]) return diff
  return {
    ...diff,
    [key]: o2[key]
  }
}, {})
Run Code Online (Sandbox Code Playgroud)

  • [Object.fromEntries()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/fromEntries) 使这个解决方案更加干净`Object.fromEntries(Object.entries( o2).filter(([k, v]) =&gt; o1[k] !== v))` (8认同)
  • 是的,这不是递归@Spurious (3认同)
  • 很好的解决方案,但你可能想检查`if(o1 [key] === o1 [key])`line dude (2认同)
  • 我喜欢该解决方案,但是它有一个问题,如果对象深于一个层次,它将返回更改后的嵌套对象中的所有值-或至少这就是我正在发生的事情。 (2认同)
  • 请记住,使用此解决方案,对于对象中的每个元素,您将获得一个全新的对象,该对象将所有现有元素复制到其中,只是向数组添加一项。对于较小的对象,这很好,但是对于较大的对象,它将成倍地降低速度。 (2认同)

tos*_*gic 21

使用Lodash:

_.mergeWith(oldObj, newObj, function (objectValue, sourceValue, key, object, source) {
    if ( !(_.isEqual(objectValue, sourceValue)) && (Object(objectValue) !== objectValue)) {
        console.log(key + "\n    Expected: " + sourceValue + "\n    Actual: " + objectValue);
    }
});
Run Code Online (Sandbox Code Playgroud)

我不使用key/object/source但是如果你需要访问它我就把它留在那里.对象比较只是阻止控制台将差异从最外层元素打印到控制台到最内层元素.

您可以在内部添加一些逻辑来处理数组.也许首先对数组进行排序.这是一个非常灵活的解决方案.

编辑

由于lodash更新,从_.merge更改为_.mergeWith.感谢Aviron注意到这一变化.

  • 不提供反馈,不要否定我.我是新人,越多人只是简单的抽搐,我想要为这个网站贡献的越少. (63认同)
  • 在lodash 4.15.0中,不再支持带定制器功能的_.merge,因此您应该使用_.mergeWith. (6认同)
  • 这个功能很棒,但不适用于嵌套对象。 (2认同)

小智 17

const diff = require("deep-object-diff").diff;
let differences = diff(obj2, obj1);
Run Code Online (Sandbox Code Playgroud)

有一个 npm 模块,每周下载量超过 50 万:https : //www.npmjs.com/package/deep-object-diff

我喜欢对象之类的差异表示 - 特别是在格式化时很容易看到结构。

const diff = require("deep-object-diff").diff;

const lhs = {
  foo: {
    bar: {
      a: ['a', 'b'],
      b: 2,
      c: ['x', 'y'],
      e: 100 // deleted
    }
  },
  buzz: 'world'
};

const rhs = {
  foo: {
    bar: {
      a: ['a'], // index 1 ('b')  deleted
      b: 2, // unchanged
      c: ['x', 'y', 'z'], // 'z' added
      d: 'Hello, world!' // added
    }
  },
  buzz: 'fizz' // updated
};

console.log(diff(lhs, rhs)); // =>
/*
{
  foo: {
    bar: {
      a: {
        '1': undefined
      },
      c: {
        '2': 'z'
      },
      d: 'Hello, world!',
      e: undefined
    }
  },
  buzz: 'fizz'
}
*/
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,它看起来从未添加“移动”支持,这对于保留对象引用非常重要,而不是盲目地重建恰好具有相同对象形状的惰性内容。 (3认同)

a11*_*les 10

我知道我参加聚会迟到了,但我需要类似的东西,但上述答案没有帮助。

我使用 Angular 的 $watch 函数来检测变量的变化。我不仅需要知道变量的属性是否已更改,而且还想确保更改的属性不是临时的计算字段。换句话说,我想忽略某些属性。

这是代码:

function diff(obj1,obj2,exclude) {
        var r = {};
    
    if (!exclude)   exclude = [];
    
    for (var prop in obj1) {
      if (obj1.hasOwnProperty(prop) && prop != '__proto__') {
            if (exclude.indexOf(obj1[prop]) == -1) {

            // check if obj2 has prop
            if (!obj2.hasOwnProperty(prop)) r[prop] = obj1[prop];

            // check if prop is object and 
            // NOT a JavaScript engine object (i.e. __proto__), if so, recursive diff
            else if (obj1[prop] === Object(obj1[prop])) {
              var difference = diff(obj1[prop], obj2[prop]);
              if (Object.keys(difference).length > 0) r[prop] = difference;
            }

            // check if obj1 and obj2 are equal
            else if (obj1[prop] !== obj2[prop]) {
                if (obj1[prop] === undefined)
                r[prop] = 'undefined';
                if (obj1[prop] === null)
                r[prop] = null;
              else if (typeof obj1[prop] === 'function')
                r[prop] = 'function';
              else if (typeof obj1[prop] === 'object')  
                r[prop] = 'object';
              else
                  r[prop] = obj1[prop];
            }
          }
       }
       
    }
    
    return r;
}
Run Code Online (Sandbox Code Playgroud)

https://jsfiddle.net/rv01x6jo/


使用方法如下:

// To only return the difference
var difference = diff(newValue, oldValue);  

// To exclude certain properties
var difference = diff(newValue, oldValue, [newValue.prop1, newValue.prop2, newValue.prop3]);
Run Code Online (Sandbox Code Playgroud)

希望这对某人有帮助。


Ana*_*ant 9

这是一个JavaScript库,可用于在两个JavaScript对象之间查找diff:

Github网址: https ://github.com/cosmicanant/recursive-diff

Npmjs网址: https ://www.npmjs.com/package/recursive-diff

recursiveDiff

您可以在浏览器中使用recursive-diff库以及Node.js. 对于浏览器,请执行以下操作:

<script type="text" src="https://unpkg.com/recursive-diff@1.0.0/dist/recursive-diff.min.js"/>
<script type="text/javascript">
     const ob1 = {a:1, b: [2,3]};
     const ob2 = {a:2, b: [3,3,1]};
     const delta = recursiveDiff.getDiff(ob1,ob2); 
     /* console.log(delta) will dump following data 
     [
         {path: ['a'], op: 'update', val: 2}
         {path: ['b', '0'], op: 'update',val: 3},
         {path: ['b',2], op: 'add', val: 1 },
     ]
      */
     const ob3 = recursiveDiff.applyDiff(ob1, delta); //expect ob3 is deep equal to ob2
 </script>
Run Code Online (Sandbox Code Playgroud)

而在node.js中,您可以使用'recursive-diff'模块并使用它,如下所示:

const diff = require('recursive-diff');
const ob1 = {a: 1}, ob2: {b:2};
const diff = diff.getDiff(ob1, ob2);
Run Code Online (Sandbox Code Playgroud)


shi*_*or7 9

我修改了@sbgoran 的答案,以便生成的 diff 对象仅包含更改的值,并省略相同的值。此外,它还显示原始值和更新值

var deepDiffMapper = function () {
    return {
        VALUE_CREATED: 'created',
        VALUE_UPDATED: 'updated',
        VALUE_DELETED: 'deleted',
        VALUE_UNCHANGED: '---',
        map: function (obj1, obj2) {
            if (this.isFunction(obj1) || this.isFunction(obj2)) {
                throw 'Invalid argument. Function given, object expected.';
            }
            if (this.isValue(obj1) || this.isValue(obj2)) {
                let returnObj = {
                    type: this.compareValues(obj1, obj2),
                    original: obj1,
                    updated: obj2,
                };
                if (returnObj.type != this.VALUE_UNCHANGED) {
                    return returnObj;
                }
                return undefined;
            }

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

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

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

                let mapValue = this.map(undefined, obj2[key]);
                if (mapValue) {
                    diff[key] = mapValue;
                }
            }

            //2020-06-13: object length code copied from /sf/answers/923368701/
            if (Object.keys(diff).length > 0) {
                return diff;
            }
            return undefined;
        },
        compareValues: function (value1, value2) {
            if (value1 === value2) {
                return this.VALUE_UNCHANGED;
            }
            if (this.isDate(value1) && this.isDate(value2) && value1.getTime() === value2.getTime()) {
                return this.VALUE_UNCHANGED;
            }
            if (value1 === undefined) {
                return this.VALUE_CREATED;
            }
            if (value2 === undefined) {
                return this.VALUE_DELETED;
            }
            return this.VALUE_UPDATED;
        },
        isFunction: function (x) {
            return Object.prototype.toString.call(x) === '[object Function]';
        },
        isArray: function (x) {
            return Object.prototype.toString.call(x) === '[object Array]';
        },
        isDate: function (x) {
            return Object.prototype.toString.call(x) === '[object Date]';
        },
        isObject: function (x) {
            return Object.prototype.toString.call(x) === '[object Object]';
        },
        isValue: function (x) {
            return !this.isObject(x) && !this.isArray(x);
        }
    }
}();
Run Code Online (Sandbox Code Playgroud)


B T*_*B T 8

目前,有很多模块可用于此.我最近写了一个模块来做这个,因为我对我发现的众多差异模块不满意.它名为odiff:https://github.com/Tixit/odiff.我还列出了一些最流行的模块,以及为什么它们在自述文件中不可接受,odiff如果odiff没有你想要的属性,你可以看一看.这是一个例子:

var a = [{a:1,b:2,c:3},              {x:1,y: 2, z:3},              {w:9,q:8,r:7}]
var b = [{a:1,b:2,c:3},{t:4,y:5,u:6},{x:1,y:'3',z:3},{t:9,y:9,u:9},{w:9,q:8,r:7}]

var diffs = odiff(a,b)

/* diffs now contains:
[{type: 'add', path:[], index: 2, vals: [{t:9,y:9,u:9}]},
 {type: 'set', path:[1,'y'], val: '3'},
 {type: 'add', path:[], index: 1, vals: [{t:4,y:5,u:6}]}
]
*/
Run Code Online (Sandbox Code Playgroud)


Jav*_*ome 8

这是一个解决方案:

  • 打字稿(但很容易转换为 Javascript)
  • 没有库依赖
  • 通用,不关心检查对象类型(除了object类型)
  • 支持有值的属性 undefined
  • 不深(默认)

首先我们定义比较结果接口:

export interface ObjectComparison {
  added: {};
  updated: {
    [propName: string]: Change;
  };
  removed: {};
  unchanged: {};
}
Run Code Online (Sandbox Code Playgroud)

在变化的特殊情况下,我们想知道什么是旧值和新值:

export interface Change {
  oldValue: any;
  newValue: any;
}
Run Code Online (Sandbox Code Playgroud)

然后我们可以提供diff只有两个循环的函数(如果deep是,则具有递归性true):

export class ObjectUtils {

  static diff(o1: {}, o2: {}, deep = false): ObjectComparison {
    const added = {};
    const updated = {};
    const removed = {};
    const unchanged = {};
    for (const prop in o1) {
      if (o1.hasOwnProperty(prop)) {
        const o2PropValue = o2[prop];
        const o1PropValue = o1[prop];
        if (o2.hasOwnProperty(prop)) {
          if (o2PropValue === o1PropValue) {
            unchanged[prop] = o1PropValue;
          } else {
            updated[prop] = deep && this.isObject(o1PropValue) && this.isObject(o2PropValue) ? this.diff(o1PropValue, o2PropValue, deep) : {newValue: o2PropValue};
          }
        } else {
          removed[prop] = o1PropValue;
        }
      }
    }
    for (const prop in o2) {
      if (o2.hasOwnProperty(prop)) {
        const o1PropValue = o1[prop];
        const o2PropValue = o2[prop];
        if (o1.hasOwnProperty(prop)) {
          if (o1PropValue !== o2PropValue) {
            if (!deep || !this.isObject(o1PropValue)) {
              updated[prop].oldValue = o1PropValue;
            }
          }
        } else {
          added[prop] = o2PropValue;
        }
      }
    }
    return { added, updated, removed, unchanged };
  }

  /**
   * @return if obj is an Object, including an Array.
   */
  static isObject(obj: any) {
    return obj !== null && typeof obj === 'object';
  }
}
Run Code Online (Sandbox Code Playgroud)

例如,调用:

ObjectUtils.diff(
  {
    a: 'a', 
    b: 'b', 
    c: 'c', 
    arr: ['A', 'B'], 
    obj: {p1: 'p1', p2: 'p2'}
  },
  {
    b: 'x', 
    c: 'c', 
    arr: ['B', 'C'], 
    obj: {p2: 'p2', p3: 'p3'}, 
    d: 'd'
  },
);
Run Code Online (Sandbox Code Playgroud)

将返回:

{
  added: {d: 'd'},
  updated: {
    b: {oldValue: 'b', newValue: 'x'},
    arr: {oldValue: ['A', 'B'], newValue: ['B', 'C']},
    obj: {oldValue: {p1: 'p1', p2: 'p2'}, newValue: {p2: 'p2', p3: 'p3'}}
  },
  removed: {a: 'a'},
  unchanged: {c: 'c'},
}
Run Code Online (Sandbox Code Playgroud)

并使用deep第三个参数调用相同的将返回:

{
  added: {d: 'd'},
  updated: {
    b: {oldValue: 'b', newValue: 'x'},
    arr: {
      added: {},
      removed: {},
      unchanged: {},
      updated: {
        0: {oldValue: 'A', newValue: 'B'},
        1: {oldValue: 'B', newValue: 'C', }
      }
    },
    obj: {
      added: {p3: 'p3'},
      removed: {p1: 'p1'},
      unchanged: {p2: 'p2'},
      updated: {}
    }
  },
  removed: {a: 'a'},
  unchanged: {c: 'c'},
}
Run Code Online (Sandbox Code Playgroud)


小智 6

另一个基于 lodash 的解决方案,当我们想要查看对对象进行的更新的差异时,它有点特定于这种情况:

const diff = return {
  old: _.pickBy(oldObject, (value, key) => { return !_.isEqual(value, newObject[key]); }),
  new: _.pickBy(newObject, (value, key) => { return !_.isEqual(oldObject[key], value); })
}
Run Code Online (Sandbox Code Playgroud)

_.omitBy由于性能影响而未使用。


per*_*mon 5

2022 年,一种简单的算法可以有效处理比较两个对象的边缘情况,并给出差异。它通过展平对象并比较第一个也是唯一一个键/值级别来实现这一点。

步骤:

  • 将物体压平。
  • 比较两个展平的对象并生成展平的差异对象。
  • 展开 diff 对象。

如果保存展平的对象,则可以重复该过程并仅在必要时取消展平 diff 对象。

下面是该算法在 JavaScript 中的实现:

let oldObject = {var1:'value1', var2:{ var1:'value1', var2:'value2'},var3:'value3'};
let newObject = {var2:{ var1:'value11', var3:'value3'},var3:'value3'};

let flatOldObject = flattenObject(oldObject)
/*
{
 'var1':'value1',
 'var2.var1':'value1',
 'var2.var2':'value2',
 'var3':'value3' 
}
*/
let flatNewObject = flattenObject(newObject)
/*
{
 'var2.var1':'value11',
 'var2.var3':'value3',
 'var3':'value3'
}
*/
let flatDiff = diffFlatten(flatOldObject, flatNewObject)
let [updated,removed] = flatDiff
/*
updated = {
 'var2.var1':'value11',
 'var2.var3':'value3'
}
removed = {
'var1':'value1'
}
*/
Run Code Online (Sandbox Code Playgroud)

请注意,如果您愿意,可以使用自己的步骤实现。以下是我使用的实现:

实施

function flattenObject(obj) {
 const object = Object.create(null);
 const path = [];
 const isObject = (value) => Object(value) === value;

 function dig(obj) {
  for (let [key, value] of Object.entries(obj)) {
    path.push(key);
    if (isObject(value)) dig(value);
    else object[path.join('.')] = value;
    path.pop();
  }
 }

 dig(obj);
 return object;
}
Run Code Online (Sandbox Code Playgroud)
function diffFlatten(oldFlat, newFlat) {
    const updated = Object.assign({}, oldFlat);
    const removed = Object.assign({}, newFlat);

    /**delete the unUpdated keys*/
    for (let key in newFlat) {
        if (newFlat[key] === oldFlat[key]) {
             delete updated[key];
             delete removed[key];
        }
    }

    return [updated, removed];

}
Run Code Online (Sandbox Code Playgroud)
function unflatenObject(flattenObject) {
    const unFlatten = Object.create(null);
    for (let [stringKeys, value] of Object.entries(flattenObject)) {
        let chain = stringKeys.split('.')
        let object = unFlatten

        for (let [i, key] of chain.slice(0, -1).entries()) {
            if (!object[key]) {
                let needArray = Number.isInteger(Number(chain[+i + 1]))
                object[key] = needArray ? [] : Object.create(null)
            }
            object = object[key];
        }
        let lastkey = chain.pop();
        object[lastkey] = value;
    }
    return unFlatten;
}
Run Code Online (Sandbox Code Playgroud)

  • 这种模式是有道理的,但对任何查看它的人来说都是一个警告:如果键里面有句点,这可能会被破坏。这是一种边缘情况,但值得考虑。 (2认同)