Skip to content

Instantly share code, notes, and snippets.

@jt0dd
Created October 8, 2021 20:58
Show Gist options
  • Save jt0dd/2f2143400a05528edac3ee4719547395 to your computer and use it in GitHub Desktop.
Save jt0dd/2f2143400a05528edac3ee4719547395 to your computer and use it in GitHub Desktop.
function isObjNotArray(obj) {
return obj && typeof obj == 'object' && !Array.isArray(obj)
}
function isIterable(obj) {
let type = false
if (isObjNotArray(obj)) type = 'obj'
else if (Array.isArray(obj)) type = 'arr'
return type
}
function mergeDeep(source, target) {
if (source === undefined) return target
if (target === undefined) return source
let allProps = [],
sourceProps, targetProps, sourceType, targetType, type, merged, iterable, filler
sourceType = isIterable(source)
targetType = isIterable(target)
type = sourceType
if (source && target && sourceType != targetType) {
throw 'source, target type mismatch'
}
if (sourceType == 'obj') {
sourceProps = Object.keys(source)
} else if (sourceType == 'arr') {
sourceProps = source
} else {
return source
}
if (targetType == 'obj') {
targetProps = Object.keys(target)
} else if (targetType = 'arr') {
targetProps = target;
} else {
throw 'mergeDeep target must be an Object or Array'
}
sourceProps.forEach(prop => {
allProps.push(prop);
})
targetProps.forEach(prop => {
allProps.push(prop);
})
allProps = [...new Set(allProps)]
if (type == 'obj') {
merged = {}
} else if (type == 'arr') {
merged = []
}
allProps.forEach(prop => {
if (type == 'obj') {
if (source[prop]) {
if (isIterable(source[prop])) {
if (isIterable(target[prop])) {
merged[prop] = mergeDeep(source[prop], target[prop])
} else {
merged[prop] = source[prop]
}
} else {
merged[prop] = source[prop]
}
} else {
if (source[prop] !== undefined) {
merged[prop] = source[prop]
} else {
merged[prop] = target[prop]
}
}
} else {
iterable = isIterable(prop)
if (iterable) {
if (iterable == 'obj') {
filler = {}
} else if (iterable == 'arr') {
filler = []
}
merged.push(mergeDeep(prop, filler))
} else {
merged.push(prop)
}
}
})
return merged
}
const parent1 = {
visualTraits: {
color: 'black',
size: 'large'
},
personalityTraits: {
friendliness: 5
}
}
const parent2 = {
personalityTraits: {
friendliness: 4
}
}
const parent3 = {
visualTraits: {
color: 'brown',
size: 'medium'
},
personalityTraits: {
friendliness: 5
}
}
const parent4 = {
personalityTraits: {
friendliness: 4
},
visualTraits: {
size: 'small'
}
}
const child1 = mergeDeep(parent1, parent2)
const child2 = mergeDeep(parent3, parent4)
const grandchild = mergeDeep(child1, child2)
console.log('child1', child1)
console.log('child2', child2)
console.log('grandchild', grandchild)
// I want any propoerties to be references to their original
// ancestor object merged from
// so that if I change:
parent1.visualTraits.color = "pink"
// grandchild with that inherited "black" color property
// passed (desireably but not in this case) by reference
// to the original parent1 will also see this change. Howver:
console.log('grandchild.visualTraits.color', grandchild.visualTraits.color) // black
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment