【问题标题】:how to rescursively deep merge an array of objects如何递归深度合并对象数组
【发布时间】:2017-12-14 13:37:41
【问题描述】:

我想合并两个对象数组。这些对象具有相同的结构,但其中一个缺少 hide 属性。我想将 hide 属性的值从一个对象复制到另一个缺少此属性的对象。重要的是我不想改变这些数组中的任何一个!

第一个数组是这样的(注意有 hide 属性):

let first_array = [
    {
        name: 'John',
        age: 40,
        hide: true,
        childs: [
            {
                name: 'Alice',
                age: 20,
                hide: false,
                childs: [
                    {
                        name: 'Mike',
                        age: 2,
                        hide: true
                    }
                ]
            }
        ]
    },
    {
        name: 'Peter',
        age: 40,
        hide: true,
        childs: [
            {
                name: 'Andrew',
                age: 20,
                hide: true,
                childs: [
                    {
                        name: 'Jessica',
                        age: 2,
                        hide: true
                    }
                ]
            }
        ]
    }
]

第二个数组看起来几乎一样!唯一缺少的是隐藏属性。

let second_array = [
    {
        name: 'John',
        age: 40,
        childs: [
            {
                name: 'Alice',
                age: 20,
                childs: [
                    {
                        name: 'Mike',
                        age: 2,
                    }
                ]
            }
        ]
    },
    {
        name: 'Peter',
        age: 40,
        childs: [
            {
                name: 'Andrew',
                age: 20,
                childs: [
                    {
                        name: 'Jessica',
                        age: 2,
                    }
                ]
            }
        ]
    }
]

现在,我想创建一个新数组,其中每个对象中都有隐藏属性。

我知道如何以 imperative 的方式递归地执行此操作,但不幸的是我正在改变数据 - 我不想这样做。

function getHideProperty(first, second) {
    for (let i = 0; i < second.length; i++) {
        for (let j = 0; j < first.length; j++) {
            if (second[i].name === first[j].name) {
                second[i].hide = first[j].hide
                if (second[i].childs) {
                    second[i].childs = getHideProperty(first[j].childs, second[i].childs)
                }
            }
        }
    }
    return second
}

现在我可以创建包含合并对象的新数组:

const newArray = getHideProperty(second_array, first_array)

现在,second_array 中的每个对象都有 hide 属性。但我改变了数组:(

如何在不改变数组的情况下达到这样的结果?

【问题讨论】:

  • 从技术上讲,当然,您不会改变数组。您正在改变数组引用的对象。就解决方案的思考而言,这很重要。
  • 所谓的mutating是指某个数组的值一开始是X,传给函数后,这个数组的值就不再是X了。变异了
  • 好吧,同样,它不是(数组中的值 - references 对象 - 在您的示例中未更改),但它仅在以下方面很重要帮助了解避免它需要什么:创建新对象(和新数组)。 :-)

标签: javascript dictionary filter functional-programming reduce


【解决方案1】:

您需要:

  1. 创建一个新数组来存储新信息,并将其返回

  2. 深拷贝second[i] 以存储在新数组中,然后再修改任何内容

对于#2,从What is the most efficient way to deep clone an object in JavaScript?中选择您最喜欢的答案

对于#1,非常粗略(参见 cmets):

function getHideProperty(first, second) {
    const result = []; // Our result array
    for (let i = 0; i < second.length; i++) {
        const secondEntry = result[i] = deepCopy(second[i]); // The deep copy, and let's avoid constantly re-retrieving second[i]/result[i]
        for (let j = 0; j < first.length; j++) {
            if (secondentry.name === first[j].name) {
                secondentry.hide = first[j].hide
                if (secondEntry.childs) {
                    // Could be more efficient here, since the entries in `childs` are already copies; left as an exercise to the reader...
                    secondEntry.childs = getHideProperty(first[j].childs, secondEntry.childs)
                }
            }
        }
    }
    return result;
}

这并不是一个全能唱歌、全能跳舞的解决方案。它旨在帮助您一路走来。请注意deepCopy 占位符,这是您对#2 的首选解决方案。 :-)


如果您执行上述操作(嵌套循环)并发现这是一个性能问题,您可以在 first 中创建一个 Map,并以它们的名称作为关键字,然后在映射时查找它们循环通过second(而不是嵌套循环)。复杂性仅在如果使用简单的嵌套循环解决方案遇到性能问题时才有用。

【讨论】:

  • 哇。非常感谢您的帮助和提示。我将 Object.assign 用于 deepCopy,它就像一个魅力。是的,我正在改变数组中的对象,而不是数组本身。我会考虑在这里使用地图
  • @sympi:注意Object.assign 不是深拷贝,因此不会复制任何从属对象(例如childs 数组),它们将被共享。 Mikael 的回答通过为新对象提供一个新的 childs 属性来避免这个问题,这对于您声明的数据来说很好;但除非你对所有从属对象明确地这样做,否则你会得到丑陋的串扰。
  • 嗯,所以这实际上是非常奇怪的。在我的真实数据中,有 9 个深度级别,我正在使用对象扩展(最终被转译为 Object.assign?)进行复制,看起来工作正常。
  • @sympi:如果有多个级别也没关系,反正你要替换childs。问题是如果你在这些上有另一个从属对象,你没有替换它。也许你现在不知道;但是,如果您稍后添加一个,您可能不会考虑返回并修复此问题,然后您最终会在新旧数据之间出现丑陋的交叉引用。
  • 好的,再次感谢您的帮助!我将使用 lodash 中的 cloneDeep
【解决方案2】:

这是一种不会改变任何原始数组或其项的函数式方法:

function getHideProperty(first, second) {
    return second.map(function(item) {
        var corresponding = first.find(function(searchItem) {
            return searchItem.name === item.name;
        });
        return Object.assign({},
          item,
          { hide: corresponding.hide },
          item.childs
            ? { childs: getHideProperty(item.childs, corresponding.childs) } 
            : {}
        );
    });
}

【讨论】:

  • 然而,它是脆弱的(结构的微小变化会导致新数组引用旧对象的一部分),并且涉及创建许多额外的临时对象。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-14
  • 2019-05-08
  • 1970-01-01
  • 2016-08-16
  • 1970-01-01
  • 2014-05-07
  • 2020-08-04
相关资源
最近更新 更多