I have this simple nested object which I need to flatten to be able to insert it into my database.
const input = {
name: "Benny",
department: {
section: "Technical",
branch: {
timezone: "UTC",
},
},
company: [
{
name: "SAP",
customers: ["Ford-1", "Nestle-1"],
},
{
name: "SAP",
customers: ["Ford-2", "Nestle-2"],
},
],
};
The desired result is like this, each value in the arrays results in a new sub-object stored in an array:
[
{
name: "Benny",
"department.section": "Technical",
"department.branch.timezone": "UTC",
"company.name": "SAP",
"company.customers": "Ford-1",
},
{
name: "Benny",
"department.section": "Technical",
"department.branch.timezone": "UTC",
"company.name": "SAP",
"company.customers": "Nestle-1",
},
{
name: "Benny",
"department.section": "Technical",
"department.branch.timezone": "UTC",
"company.name": "SAP",
"company.customers": "Ford-2",
},
{
name: "Benny",
"department.section": "Technical",
"department.branch.timezone": "UTC",
"company.name": "SAP",
"company.customers": "Nestle-2",
},
]
Instead of the result below which all fields stored in single object with indexes:
{
name: 'Benny',
'department.section': 'Technical',
'department.branch.timezone': 'UTC',
'company.0.name': 'SAP',
'company.0.customers.0': 'Ford-1',
'company.0.customers.1': 'Nestle-1',
'company.1.name': 'SAP',
'company.1.customers.0': 'Ford-2',
'company.1.customers.1': 'Nestle-2'
}
My code looks like this:
function flatten(obj) {
let keys = {};
for (let i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == "object") {
let flatObj = flatten(obj[i]);
for (let j in flatObj) {
if (!flatObj.hasOwnProperty(j)) continue;
keys[i "." j] = flatObj[j];
}
} else {
keys[i] = obj[i];
}
}
return keys;
}
Thanks in advance!
CodePudding user response:
The closest (legitimate) I could get to your desired result is:
[
{
"name": "Benny",
"department.section": "Technical",
"department.branch.timezone": "UTC",
"company.name": "SAP",
"company.customers.0": "Ford-1",
"company.customers.1": "Nestle-1"
},
{
"name": "Benny",
"department.section": "Technical",
"department.branch.timezone": "UTC",
"company.name": "SAP",
"company.customers.0": "Ford-2",
"company.customers.1": "Nestle-2"
}
]
I had to create a wrapper function called flattenBy
that handles mapping the data by a particular key e.g. company
and passes it down to your flatten
function (along with the current index).
const flatten = (obj, key, index) => {
let keys = {};
for (let i in obj) {
if (!obj.hasOwnProperty(i)) continue;
let ref = i !== key ? obj[i] : obj[i][index];
if (typeof ref == 'object') {
let flatObj = flatten(ref, key);
for (let j in flatObj) {
if (!flatObj.hasOwnProperty(j)) continue;
keys[i '.' j] = flatObj[j];
}
} else { keys[i] = obj[i]; }
}
return keys;
}
const flattenBy = (obj, key) =>
obj[key].map((item, index) => flatten(obj, key, index));
const input = {
name: "Benny",
department: { section: "Technical", branch: { timezone: "UTC" } },
company: [
{ name: "SAP", customers: ["Ford-1", "Nestle-1"] },
{ name: "SAP", customers: ["Ford-2", "Nestle-2"] },
]
};
console.log(JSON.stringify(flattenBy(input, 'company'), null, 2));
.as-console-wrapper { top: 0; max-height: 100% !important; }
CodePudding user response:
You could keep the indices for nested arrays.
const
getFlat = o => Object.entries(o).flatMap(([k, v]) => v && typeof v === 'object'
? getFlat(v).map(([l, v]) => [`${k}.${l}`, v])
: [[k, v]]
),
fn = ({ company, ...o }) => {
const rest = Object.fromEntries(getFlat(o));
return company.map(company => ({
...rest,
...Object.fromEntries(getFlat({ company }))
}));
}
input = { name: "Benny", department: { section: "Technical", branch: { timezone: "UTC" } }, company: [{ name: "SAP", customers: ["Ford-1", "Nestle-1"] }, { name: "SAP", customers: ["Ford-2", "Nestle-2"] }] },
result = fn(input);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }