Bahroze Ali
Bahroze Ali

Reputation: 123

Merge nested JavaScript objects based on same nested key

I've been trying to turn the following input:

{
    "list": [
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "angular": {
                    "html": ""
                }
            }
        },
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "angular": {
                    "script": ""
                }
            }
        },
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "html": {
                    "html": ""
                }
            }
        },
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "html": {
                    "script": ""
                }
            }
        },
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "react": {
                    "script": ""
                }
            }
        },
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "vue": {
                    "script": ""
                }
            }
        },
    {
        "name": "light_with_header",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "vue": {
                "script": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "angular": {
                "html": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "angular": {
                "script": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "html": {
                "html": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "html": {
                "script": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "react": {
                "script": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "vue": {
                "script": ""
            }
        }
    },

    ]
}

To the output :

{
    list: [
        {
            name: "light_with_header",
            path: "webapp/master_layouts/sidebar_layouts/",
            type: "Directory",
            data: {
                angular: {
                    html: "",
                    script: ""
                },
                html: {
                    html: "",
                    script: ""
                },
                react: {
                    script: ""
                },
                vue: {
                    "script": ""
                }
            }
        },
        {
          name: "light_with_header_and_icons",
          path: "webapp/master_layouts/sidebar_layouts/",
          type: "Directory",
           data: {
            angular: {
                html: "",
                script: ""
            },
            html: {
                html: "",
                script: ""
            },
            react: {
                script: ""
            },
            vue: {
                script: ""
            }

       },
  
    ]
}

Following returns an empty list. It should've merged the objects if the properties are the same:

var anotherList = list.reduce((prev,next)=>{
    let newList = mergeArrayObjects(prev,next)
    return newList
},[])

function mergeArrayObjects(arr1,arr2){
    return arr1.map((item,i)=>{
       if(item.name === arr2[i].name){
           //merging two objects
         return Object.assign({},item,arr2[i])
       }
    })
}

How to acquire demonstrated output from provided input?

Upvotes: 0

Views: 104

Answers (4)

Carsten Massmann
Carsten Massmann

Reputation: 28196

Edit

This is an updated version to fulfill the requirements exactly as set above:

inp={"list": [
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"angular": {"html": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"angular": {"script": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"html": {"html": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"html": {"script": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"react": {"script": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"vue": {"script": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"vue": {"script": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"angular": {"html": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"angular": {"script": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"html": {"html": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"html": {"script": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"react": {"script": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"vue": {"script": ""}}}
]};

const res  =Object.values(inp.list.reduce((a,c)=>{ 
        //  Object.values: only return the different *values* of the collection
  let nam=c.name+c.path+c.type;    // collect "similar" objects together 
  let curr=a[nam]=a[nam]||{...c};  // create or refer to a collection element
  Object.entries(c.data).forEach(([k,v])=>curr.data[k]={...curr.data[k],...v})
  return a},{}));

console.log(res)

Previous versions...
Here is another solution to your problem (a one-liner). It differs from your requirement in the way that it builds on the first element of your input object and therefore does not cointain the name "light_with_header_and_icons" instead of "light_with_header",

const inp={"list": [
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"angular": {"html": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"angular": {"script": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"html": {"html": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"html": {"script": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"react": {"script": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"vue": {"script": ""}}},
  {"name": "light_with_header","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"vue": {"script": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"angular": {"html": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"angular": {"script": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"html": {"html": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"html": {"script": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"react": {"script": ""}}},
  {"name": "light_with_header_and_icons","path": "webapp/master_layouts/sidebar_layouts/","type": "Directory","data": {"vue": {"script": ""}}}
]}
const res=
inp.list.reduce((a,c)=>(Object.entries(c.data).forEach(([k,v])=>a.data[k]={...a.data[k],...v}),a))
console.log(res)

// alternative version:
const res2=
inp.list.reduce((a,c)=>(Object.entries(c.data).forEach(([k,v])=>a.data[k]={...a.data[k],...v}),a),inp.list[inp.list.length-1])
console.log(res2)

My "alternative" version returns an output object that is built on the last element of inp.list.

Upvotes: 2

pilchard
pilchard

Reputation: 12909

Here is straightforward reduce() using find() to check for existing entries.

const input = { "list": [{ "name": "light_with_header", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "angular": { "html": "" } } }, { "name": "light_with_header", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "angular": { "script": "" } } }, { "name": "light_with_header", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "html": { "html": "" } } }, { "name": "light_with_header", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "html": { "script": "" } } }, { "name": "light_with_header", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "react": { "script": "" } } }, { "name": "light_with_header", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "vue": { "script": "" } } }, { "name": "light_with_header", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "vue": { "script": "" } } }, { "name": "light_with_header_and_icons", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "angular": { "html": "" } } }, { "name": "light_with_header_and_icons", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "angular": { "script": "" } } }, { "name": "light_with_header_and_icons", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "html": { "html": "" } } }, { "name": "light_with_header_and_icons", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "html": { "script": "" } } }, { "name": "light_with_header_and_icons", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "react": { "script": "" } } }, { "name": "light_with_header_and_icons", "path": "webapp/master_layouts/sidebar_layouts/", "type": "Directory", "data": { "vue": { "script": "" } } },] }

const output = {};
output.list = input.list.reduce((a, o) => {
  const p = a.find(({ name }) => name === o.name);
  if (p) {
    Object.entries(o.data).forEach(([k, v]) =>
      p.data[k] = { ...p.data[k] ?? {}, ...v }
    );
  } else {
    a.push({ ...o })
  }
  return a;
}, []);

console.log(output)

Upvotes: 0

Bahroze Ali
Bahroze Ali

Reputation: 123

I've found the solution that I needed. Thank you all for the help really. I modified the cars10m. I create a new list and append all to it. My code for future..

let myList = []
list.reduce((a,c)=>{
    if(a.name !== c.name){
        myList.push(c)
        return c
    }else{
        return (Object.entries(c.data).forEach(([k,v])=>a.data[k]={...a.data[k],...v}),a)
    }
},list[list.length-1])

Upvotes: 0

Aplet123
Aplet123

Reputation: 35512

First maintain an object mapping ids to objects, then you can convert it back to a list later:

const mappings = {};
const ordering = []; // maintain ordering and path/type fields
// assuming your object is in variable obj
for (const {data, ...rest} of obj.list) {
    if (rest.name in mappings) {
        Object.assign(mappings[rest.name], data);
    } else {
        mappings[rest.name] = data;
        ordering.push(rest);
    }
}
const newObj = {list: ordering.map(x => ({...x, data: mappings[x.name]}))};

const obj = {
    "list": [
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "angular": {
                    "html": ""
                }
            }
        },
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "angular": {
                    "script": ""
                }
            }
        },
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "html": {
                    "html": ""
                }
            }
        },
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "html": {
                    "script": ""
                }
            }
        },
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "react": {
                    "script": ""
                }
            }
        },
        {
            "name": "light_with_header",
            "path": "webapp/master_layouts/sidebar_layouts/",
            "type": "Directory",
            "data": {
                "vue": {
                    "script": ""
                }
            }
        },
    {
        "name": "light_with_header",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "vue": {
                "script": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "angular": {
                "html": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "angular": {
                "script": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "html": {
                "html": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "html": {
                "script": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "react": {
                "script": ""
            }
        }
    },
    {
        "name": "light_with_header_and_icons",
        "path": "webapp/master_layouts/sidebar_layouts/",
        "type": "Directory",
        "data": {
            "vue": {
                "script": ""
            }
        }
    },

    ]
};

const mappings = {};
const ordering = []; // maintain ordering and path/type fields
// assuming your object is in variable obj
for (const {data, ...rest} of obj.list) {
    if (rest.name in mappings) {
        Object.assign(mappings[rest.name], data);
    } else {
        mappings[rest.name] = data;
        ordering.push(rest);
    }
}
const newObj = {list: ordering.map(x => ({...x, data: mappings[x.name]}))};

console.log(newObj);
.as-console-wrapper {min-height: 100%};

Upvotes: 1

Related Questions