Search code examples
javascriptmergetreelodashcollocation

How to deep merge two collections by duplicate key in JavaScript/Lodash?


I would like to merge two collections by duplicate key in javascript, here is example collections:

let collection1 = [
    {
        title: 'Overview',
        key: 'Test-overview',
        isLeaf: true
    },
    {
        title: 'Folder 1',
        key: 'Test-Folder_1',
        children: [
            {
                title: 'Folder 1 Content 1',
                key: 'Test-Folder_1-Content_1',
                isLeaf: true,
            },
        ],
    }
]


let collection2 = [
        {
            title: 'Folder 1',
            key: 'Test-Folder_1',
            children: [
                {
                    title: 'Sub Folder 1 in Folder 1',
                    key: 'Test-Folder_1-Sub_Folder_1',
                    children: [
                        {
                            title: 'Sub Folder 1 Conetent',
                            key: 'Test-Folder_1-Sub_Folder_1-Content',
                            isLeaf: true,
                        },
                    ],
                },
            ],
        }
    ]

and this is example output:

let exampleOutput = [
        {
            title: 'Overview',
            key: 'Test-overview',
            isLeaf: true
        },
        {
            title: 'Folder 1',
            key: 'Test-Folder_1',
            children: [
                {
                    title: 'Folder 1 Content 1',
                    key: 'Test-Folder_1-Content_1',
                    isLeaf: true,
                },
                {
                    title: 'Sub Folder 1 in Folder 1',
                    key: 'Test-Folder_1-Sub_Folder_1',
                    children: [
                        {
                            title: 'Sub Folder 1 Conetent',
                            key: 'Test-Folder_1-Sub_Folder_1-Content',
                            isLeaf: true,
                        },
                    ],
                },
            ],
        }
    ]

How can I achieve this output with Javascript? I tried with Lodash _.merge and _.mergeWith but the output is not what I want. I also tried this link: Merge JavaScript objects in array with same key answer by @BenG but it only able to merge the first layer of the collections, which mean if I have collection3 that contain another content in Test-Folder_1-Sub_Folder_1, it will be replaced by the first layer of the new collection.


Solution

  • You can use recursion:

    let collection1 = [{title: 'Overview', key: 'Test-overview', isLeaf: true}, {title: 'Folder 1', key: 'Test-Folder_1', children: [{title: 'Folder 1 Content 1', key: 'Test-Folder_1-Content_1', isLeaf: true, },],}]
    let collection2 = [{title: 'Folder 1', key: 'Test-Folder_1', children: [{title: 'Sub Folder 1 in Folder 1', key: 'Test-Folder_1-Sub_Folder_1', children: [{title: 'Sub Folder 1 Conetent', key: 'Test-Folder_1-Sub_Folder_1-Content', isLeaf: true,},],},],}]
    function merge(collections){
       var formed = {}
       for (var i of collections){
          key = JSON.stringify(Object.keys(i).filter(x => x != 'children').map(x => [x, i[x]]))
          if (!(key in formed)){ 
             formed[key] = []
          }
          formed[key] = [...formed[key], ...('children' in i ? i.children : [])]
       }
       return Object.keys(formed).map(x => ({...Object.fromEntries(JSON.parse(x)), ...(formed[x].length ? {'children':merge(formed[x])} : {})}))
    }
    console.log(merge([...collection1, ...collection2]))