misunderstanding
I define buildObj
before buildObjKey
and call buildObjKey
while its not defined yet... This is bad practice, but if I move definition of buildObj
after buildObjKey
I will call buildObj
before It was defined...
That's untrue. buildObj
is a function and it is not called before you define buildObjKey
. The fact that buildObj
contains a reference to buildObjKey
does not mean that it tries to immediately call it. To demonstrate this concretely, let's see a simplified example below.
Notice how isEven
and isOdd
do not produce output until one of the functions is actually called -
function isEven (n)
{ console.log("isEven", n)
if (n == 0)
return true
else
return isOdd(n - 1) // <- calls isOdd
}
function isOdd (n)
{ console.log("isOdd", n)
if (n == 0)
return false
else
return isEven(n - 1) // <- calls isEven
}
console.log("first line of output")
console.log("result", isOdd(3))
first line of output
isOdd 3
isEven 2
isOdd 1
isEven 0
result true
possible and powerful
This is possible to do recursive calls between two functions without this trouble?
This is a massively powerful technique known as mutual recursion. Mutual recursion is an excellent way to process recursive trees like the deeply-nested objects you have in your program. You have good intuition to process them this way. See this Q&A for a practical example and explanation.
related
Coincidentally I wrote a generic object diffing function in this Q&A. This demonstrates the strength of generic functions and reusable code. Using the test1
and test2
inputs from your question, we can calculate a precise diff without any modification to the original code -
const test1 = {
"common": {
"setting1": "Value 1",
"setting2": 200,
"setting3": true,
"setting6": {
"key": "value",
"doge": {
"wow": ""
}
}
},
"group1": {
"baz": "bas",
"foo": "bar",
"nest": {
"key": "value"
}
},
"group2": {
"abc": 12345,
"deep": {
"id": 45
}
}
}
const test2 = {
"common": {
"follow": false,
"setting1": "Value 1",
"setting3": null,
"setting4": "blah blah",
"setting5": {
"key5": "value5"
},
"setting6": {
"key": "value",
"ops": "vops",
"doge": {
"wow": "so much"
}
}
},
"group1": {
"foo": "bar",
"baz": "bars",
"nest": "str"
},
"group3": {
"fee": 100500,
"deep": {
"id": {
"number": 45
}
}
}
}
console.log(diff(test1, test2))
Expand the snippet below to verify the results of diff
in your own browser -
const isObject = x =>
Object(x) === x
const isArray =
Array.isArray
const mut = (o, [ k, v ]) =>
(o[k] = v, o)
const diff1 = (left = {}, right = {}, rel = "left") =>
Object
.entries(left)
.map
( ([ k, v ]) =>
isObject(v) && isObject(right[k])
? [ k, diff1(v, right[k], rel) ]
: right[k] !== v
? [ k, { [rel]: v } ]
: [ k, {} ]
)
.filter
( ([ _, v ]) =>
Object.keys(v).length !== 0
)
.reduce
( mut
, isArray(left) && isArray(right) ? [] : {}
)
const merge = (left = {}, right = {}) =>
Object
.entries(right)
.map
( ([ k, v ]) =>
isObject(v) && isObject(left [k])
? [ k, merge(left [k], v) ]
: [ k, v ]
)
.reduce(mut, left)
const diff = (x = {}, y = {}, rx = "left", ry = "right") =>
merge
( diff1(x, y, rx)
, diff1(y, x, ry)
)
const test1 = {
"common": {
"setting1": "Value 1",
"setting2": 200,
"setting3": true,
"setting6": {
"key": "value",
"doge": {
"wow": ""
}
}
},
"group1": {
"baz": "bas",
"foo": "bar",
"nest": {
"key": "value"
}
},
"group2": {
"abc": 12345,
"deep": {
"id": 45
}
}
}
const test2 = {
"common": {
"follow": false,
"setting1": "Value 1",
"setting3": null,
"setting4": "blah blah",
"setting5": {
"key5": "value5"
},
"setting6": {
"key": "value",
"ops": "vops",
"doge": {
"wow": "so much"
}
}
},
"group1": {
"foo": "bar",
"baz": "bars",
"nest": "str"
},
"group3": {
"fee": 100500,
"deep": {
"id": {
"number": 45
}
}
}
}
console.log(diff(test1, test2))
{
"common": {
"setting2": {
"left": 200
},
"setting3": {
"left": true,
"right": null
},
"setting6": {
"doge": {
"wow": {
"left": "",
"right": "so much"
}
},
"ops": {
"right": "vops"
}
},
"follow": {
"right": false
},
"setting4": {
"right": "blah blah"
},
"setting5": {
"right": {
"key5": "value5"
}
}
},
"group1": {
"baz": {
"left": "bas",
"right": "bars"
},
"nest": {
"left": {
"key": "value"
},
"right": "str"
}
},
"group2": {
"left": {
"abc": 12345,
"deep": {
"id": 45
}
}
},
"group3": {
"right": {
"fee": 100500,
"deep": {
"id": {
"number": 45
}
}
}
}
}