I have a JSON conform object structure like this ...
{
"Name": "Pedro Smith",
"Age": "8",
"Parents": {
"Mother": {
"Name": "Maria Smith",
"RG": "123456"
},
"Father": {
"Name": "Jose Smith",
"RG": "5431"
}
}
}
... and I want to process the above object into a flat representation of itself. The new object should not be nested but its former hierarchy should be reflected by its key/property names where each new key was aggregated by its deepest value's property path.
The expected structure of the above given example should look like this ...
{
"Name": "Pedro Smith",
"Age": "8",
"Parents-Mother-Name": "Maria Smith",
"Parents-Mother-RG": "123456",
"Parents-Father-Name": "Jose Smith",
"Parents-Father-RG": "5431"
}
How can such a transformation task be achieved in JavaScript?
TLDR
If one starts thinking about a solution for a non nested object structure or how to retrieve the object's first level key
-value
pairs (aka entries
) one might think, that iterating over such entries and somehow executing a logging forEach
key-value pair might be a valid first step/approach.
And since one does not want to use something like for...in
but something more readable, an example code might end up looking like this ...
const sample = {
Name: "Pedro Smith",
Age: "8",
Parents: {
Mother: {
Name: "Maria Smith",
RG: "123456",
},
Father: {
Name: "Jose Smith",
RG: "5431",
},
},
};
Object
.entries(sample)
.forEach(entry => {
const key = entry[0];
const value = entry[1];
console.log(key + ': ' + value + '\n');
});
.as-console-wrapper { min-height: 100%!important; top: 0; }
Of cause one can improve the above code by not logging every iteration step but by aggregating the final result, which could be achieved with e.g. the help of reduce
, join
and some Destructuring assignments ...
const sample = {
Name: "Pedro Smith",
Age: "8",
Parents: {
Mother: {
Name: "Maria Smith",
RG: "123456",
},
Father: {
Name: "Jose Smith",
RG: "5431",
},
},
};
console.log(
Object
.entries(sample)
.reduce((result, [key, value]) => [
result,
key + ': ' + value,
].join('\n'), '')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
But the OP wants both a flattened object structure and aggregated keys, where the latter is supposed to somehow reflect the key path into each similar formed substructure. Thus, one needs to walk the object tree/structure.
In order to do so, and since the above example already offers a solution for a simple object branch, one needs to turn the reduce
function into a recursive one.
The aggregating/accumulating first argument of reduce
will be used for collecting and configuration purposes like not just carrying the result
reference but also referring to the current's key (aggregated by a Template literal) prefix
and the connector
string ...
function recursivelyConcatKeyAndAssignFlatEntry(collector, [key, value]) {
const { connector, keyPath, result } = collector;
// concatenate/aggregate the current
// but flattened key if necessary.
key = keyPath
? `${ keyPath }${ connector }${ key }`
: key;
if (value && (typeof value === 'object')) {
// ... recursively continue flattening in case
// the current `value` is an "object" type ...
Object
.entries(value)
.reduce(recursivelyConcatKeyAndAssignFlatEntry, {
connector,
keyPath: key,
result,
});
} else {
// ... otherwise ... just assign a new flattened
// key and current value pair to the final object.
result[key] = value;
}
return collector;
}
const sample = {
Name: "Pedro Smith",
Age: "8",
Parents: {
Mother: {
Name: "Maria Smith",
RG: "123456",
},
Father: {
Name: "Jose Smith",
RG: "5431",
},
},
};
console.log(
Object
.entries(sample)
.reduce(recursivelyConcatKeyAndAssignFlatEntry, {
connector: '-',
keyPath: '',
result: {},
}).result
);
.as-console-wrapper { min-height: 100%!important; top: 0; }