I have fairly symmetrical data I want to reduce:
const data = [
{
name: 'Bob',
relations: {
siblings: [
{
name: 'Tom',
age: '20'
snacks: [
{
type: 'yogurt'
}
]
},
{
name: 'Jacob'
snacks: [
{
type: 'cookie',
amount: '2'
}
{
type: 'brownie',
amount: '3'
}
]
}
]
}
},
{
name: 'Robert',
relations: {
siblings: [
{
name: 'Timmy',
age: '16'
snacks: [
{
type: 'fudge'
}
{
type: 'brownie'
}
]
}
]
}
}
];
What I'm trying to produce:
const output = {
name: ['Bob', 'Robert'],
relations: {
siblings: {
name: ['Tom', 'Jacob', 'Timmy'],
age: ['20', '16'],
snacks: {
type: ['yogurt', 'cookie', 'fudge', 'brownie'],
amount: ['2', '3']
}
}
}
}
I understand how to do this without recursion, but I wanted a solution that would go deep. Usually I just use reduce with recursion, but then I realized that I would have to add the value to the current level of the object which I don't know how to do.
const compact = (value) => {
if (typeof value === 'string') {
return { [value]: '' }; // turning into an object for the mean time
}
if (Array.isArray(object)) { }
// if object
return Object.entries(object).reduce((accum, [key, value]) => {
// Do I have to pass accum[key] into compact here? Is this the correct way to do this?
accum[key] = compact(value);
return accum;
}, {});
};
Here is what I would propose: divide functionality into two different functions; one for summarizing a given object, and another for merging two objects that are already summarized.
Then it could be like this:
function merge(a, b) {
const keys = [...new Set([...Object.keys(a), ...Object.keys(b)])];
return Object.fromEntries(keys.map(key => [key,
!Object.hasOwn(a, key) ? b[key]
: !Object.hasOwn(b, key) ? a[key]
: Array.isArray(a[key]) ? [...new Set([...a[key], ...b[key]])]
: merge(a[key], b[key])
]));
}
function summarize(obj) {
return Object(obj) !== obj ? [obj] // Primitive is wrapped in array
: Array.isArray(obj) ? obj.reduce((acc, item) => merge(acc, summarize(item)), {})
: Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, summarize(value)]))
}
// Demo on the input in the question:
const data = [{name: 'Bob',relations: {siblings: [{name: 'Tom',age: '20'},{name: 'Jacob'}]}},{name: 'Robert',relations: {siblings: [{name: 'Timmy',age: '16'}]}}];
const result = summarize(data);
console.log(result);