all-in-one
You can write recount
using recursion. This solution depends on the leaf nodes (deepest nesting) to contain a numeric count
property -
function recount ({count, ...t}) {
if (count != null) {
return { ...t, count }
}
else {
const children =
Object.entries(t).map(([k, v]) => [k, recount(v)])
return {
count: children.reduce((r, [_, {count}]) => r + count, 0),
...Object.fromEntries(children)
}
}
}
const myinput =
{main:{sub1:{cat:{subcat1:{count:1},subcat2:{count:2}}},sub2:{cat:{subcat1:{count:3},subcat2:{count:5}}}}}
console.log(recount(myinput))
{
"count": 11,
"main": {
"count": 11,
"sub1": {
"count": 3,
"cat": {
"count": 3,
"subcat1": {
"count": 1
},
"subcat2": {
"count": 2
}
}
},
"sub2": {
"count": 8,
"cat": {
"count": 8,
"subcat1": {
"count": 3
},
"subcat2": {
"count": 5
}
}
}
}
}
decomposed
There are drawbacks to writing a large complex function that takes on many responsibilities. Here is another approach using combination of generic functions.
First we write recount
which defines only the restructuring of nodes -
const recount = t =>
t?.count
? t
: bind
( r => ({ count: count(r), ...r })
, map(t, recount)
)
Then we define how to perform the actual count
-
const count = t =>
sum(Object.values(t).map(v => v.count))
And fill in the generic dependencies, bind
, map
, and sum
-
const bind = (f, ...x) =>
f(...x)
const map = (t, f) =>
Object.fromEntries
( Object
.entries(t)
.map(([k,v]) => [k, f(v)])
)
const sum = t =>
t.reduce((r, v) => r + v, 0)
Functionality is the same -
const myinput =
{main:{sub1:{cat:{subcat1:{count:1},subcat2:{count:2}}},sub2:{cat:{subcat1:{count:3},subcat2:{count:5}}}}}
console.log(recount(myinput))
Expand the snippet below to verify the result in your own browser -
const bind = (f, ...x) =>
f(...x)
const map = (t, f) =>
Object.fromEntries
( Object
.entries(t)
.map(([k,v]) => [k, f(v)])
)
const sum = t =>
t.reduce((r, v) => r + v, 0)
const recount = t =>
t?.count
? t
: bind
( r => ({ count: count(r), ...r })
, map(t, recount)
)
const count = t =>
sum(Object.values(t).map(v => v.count))
const myinput =
{main:{sub1:{cat:{subcat1:{count:1},subcat2:{count:2}}},sub2:{cat:{subcat1:{count:3},subcat2:{count:5}}}}}
console.log(recount(myinput))
{
"count": 11,
"main": {
"count": 11,
"sub1": {
"count": 3,
"cat": {
"count": 3,
"subcat1": {
"count": 1
},
"subcat2": {
"count": 2
}
}
},
"sub2": {
"count": 8,
"cat": {
"count": 8,
"subcat1": {
"count": 3
},
"subcat2": {
"count": 5
}
}
}
}
}