递归地减去两个JavaScript对象

时间:2016-01-20 17:42:04

标签: javascript recursion

让我们拥有一个具有默认设置的对象:

var defaults = {
    id: '',
    x: 0,
    y: 0,
    width: 20,
    height: 20,
    styles: {
        color: '#ffffff',
        background_color: '#000000'
    },
    points: []
}

然后,我们创建自己的对象,它最初扩展了默认设置,并进行了一些更改:

var newObject = {
    id: '1', // changed
    x: 10, // changed
    y: 10, // changed
    width: 20,
    height: 20,
    styles: {
        color: '#ffffff',
        background_color: '#333333' // changed
    },
    points: [1, 2, 3]
}

最后,我们需要一个对象,它只包含从默认设置更改的值,如下所示:

var subtracted = {
    id: '1',
    x: 10,
    y: 10,
    styles: {
        background_color: '#333333'
    },
    points: [1, 2, 3]
}

算法需要递归,对象中可以有对象。以下是我到目前为止的情况:

function subtract(a, b) {
    var r = {};

    // For each property of 'b'
    // if it's different than the corresponding property of 'a'
    // place it in 'r'
    for (var key in b) {
        if (typeof(b[key]) == 'object') {
            if (!a[key]) a[key] = {};
            r[key] = subtract(a[key], b[key]);
        } else {
            if (b[key] != a[key]) {
                r[key] = a[key];
            }
        }
    }

    return r;
}

但是,递归不适用于数组,所以" points"原来是一个空物体! typeof()将其检测为对象,无法以某种方式克隆其属性。

https://jsfiddle.net/gd8q1u18/1/

2 个答案:

答案 0 :(得分:2)

您的代码正常运行。虽然我有一个编辑,使其也递归。

CREATE TABLE [dbo].[Receipt] (
[receiptID]          INT           IDENTITY (1, 1) NOT NULL,
[employeeUser]       NVARCHAR (50) NULL,
[purchasedProductID] INT           NULL,
[productName]        NVARCHAR (50) NULL,
[clientID]           INT           NULL,
PRIMARY KEY CLUSTERED ([receiptID] ASC)

答案 1 :(得分:1)

更新:另一种递归方法。它通过修改运行,因此newObject可以忽略某些字段。它也适用于基元。

const equalArrays = (arr1, arr2) => arr1.length === arr2.length && arr1.every((element, index) => element === arr2[index])
// notice that equalArrays matters the order of the arrays' elements.
// If order doesn't matter, consider sorting both arrays first

const isObject = (obj) => obj instanceof Object && !(obj instanceof Array)
// notice that arrays are instance of Objects too
// an unwary consumer might mix arrays and objects with unpredictable results

const isArray = (arr) => arr instanceof Array

const getDifferences = (original, modified) => {
  const areArrays = isArray(original) && isArray(modified)
  const areObjects = isObject(original) && isObject(modified)
  if (areObjects) {
    let result = {}
    for (const key of Object.keys(modified)) {
      const diff = getDifferences(original[key], modified[key])
      if (diff) result[key] = diff
    }
    return !!Object.keys(result).length && result
  }
  else if (areArrays && !equalArrays(original, modified)) return modified
  else if (original !== modified) return modified
}
// notice that some variables and functions are there for readability and might be inlined

let defaults = {
  id: '',
  x: 0,
  y: 0,
  width: 20,
  height: 20,
  styles: {
    color: '#ffffff',
    background_color: '#000000'
  },
  points: []
}

let newObject = {
  id: '1', // changed
  x: 10, // changed
  y: 10, // changed
  width: 20,
  height: 20,
  styles: {
    color: '#ffffff',
    background_color: '#333333' // changed
  },
  points: [0, 1, 2] // changed
}

console.log(getDifferences(defaults, newObject))

我会考虑到一些粗心的消费者可能会混合使用数组和对象。

const equalArrays = (arr1, arr2) => arr1.length === arr2.length && arr1.every((element, index) => element === arr2[index])
// notice that equalArrays matters the order of the arrays' elements.
// If order doesn't matter, consider sorting both arrays first

const isObject = (obj) => obj instanceof Object && !(obj instanceof Array)
// notice that arrays are instance of Objects too
// an unwary consumer might mix arrays and objects with unpredictable results

const isArray = (arr) => arr instanceof Array

const getDifferences = (obj1, obj2) => {
  let obj3 = {}
  for (const key of Object.keys(obj1)) {
    const val1 = obj1[key]
    const val2 = obj2[key]
    const areArrays = isArray(val1) && isArray(val2)
    const areObjects = isObject(val1) && isObject(val2)
    if (areObjects) {
      const diff = getDifferences(val1, val2)
      if (diff) obj3[key] = diff
    }
    else if (areArrays && !equalArrays(val1, val2)) obj3[key] = val2
    else if (val1 !== val2) obj3[key] = val2
  }
  return !!Object.keys(obj3).length && obj3
}
// notice that some variables and functions are there for readability and might be inlined

let defaults = {
  id: '',
  x: 0,
  y: 0,
  width: 20,
  height: 20,
  styles: {
    color: '#ffffff',
    background_color: '#000000'
  },
  points: []
}

let newObject = {
  id: '1', // changed
  x: 10, // changed
  y: 10, // changed
  width: 20,
  height: 20,
  styles: {
    color: '#ffffff',
    background_color: '#333333' // changed
  },
  points: [0, 1, 2] // changed
}

console.log(getDifferences(defaults, newObject))