misconception
Prior to defining buildObjKey
, I define buildObj
and then attempt to call buildObjKey
. This practice is considered poor, but moving the definition of buildObj
after buildObjKey
would result in a situation where buildObj
is called before it is defined...
This belief is inaccurate. buildObj
is actually a function and is not invoked prior to the definition of buildObjKey
. Just because buildObj
references buildObjKey
does not mean an immediate call is made. To illustrate this clearly, let's examine a simplified example below.
Take note that isEven
and isOdd
do not generate output until one of the functions is explicitly 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
feasible and robust
Is it possible to have recursive calls between two functions without encountering issues?
Indeed, this technique known as mutual recursion is extremely powerful. Mutual recursion proves to be an effective method for processing recursive trees like the deeply-nested objects within your program. Your utilization of this approach showcases strong intuition. Refer to this Q&A for a practical example and explanation.
relevant
Coincidentally, I crafted a versatile object comparison function in this Q&A. This serves as a demonstration of the effectiveness of generic functions and reusable code. By using the inputs test1
and test2
from your query, we can accurately compute a difference without altering 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
}
}
}
}
}