core-functions
Advanced tools
Comparing version 2.0.5 to 2.0.6
@@ -13,3 +13,4 @@ 'use strict'; | ||
valueOf: valueOf, | ||
merge: merge | ||
merge: merge, | ||
copy: copy | ||
}; | ||
@@ -37,24 +38,66 @@ | ||
function merge(from, to, replace, deep) { | ||
const fromNames = Object.getOwnPropertyNames(from); | ||
const toNames = Object.getOwnPropertyNames(to); | ||
for (let i = 0; i < fromNames.length; ++i) { | ||
const name = fromNames[i]; | ||
const history = new WeakMap(); | ||
const fromProp = from[name]; | ||
const fromPropIsObject = fromProp && typeof fromProp === 'object'; | ||
function mergeWithHistory(src, dest) { | ||
if (history.has(src)) { | ||
return history.get(src); | ||
} | ||
// Remember this merge in case the same source appears again within its own graph | ||
history.set(src, dest); | ||
const existsOnTo = toNames.indexOf(name) !== -1; | ||
const srcNames = Object.getOwnPropertyNames(src); | ||
const destNames = Object.getOwnPropertyNames(dest); | ||
for (let i = 0; i < srcNames.length; ++i) { | ||
const name = srcNames[i]; | ||
if (existsOnTo) { | ||
const toProp = to[name]; | ||
if (deep && fromPropIsObject && toProp && typeof toProp === 'object') { | ||
merge(fromProp, toProp, replace, deep); | ||
} else if (replace) { | ||
to[name] = fromPropIsObject ? merge(fromProp, {}) : fromProp; | ||
const srcPropertyValue = src[name]; | ||
const srcPropertyIsObject = srcPropertyValue && typeof srcPropertyValue === 'object'; | ||
const existsOnDest = destNames.indexOf(name) !== -1; | ||
console.log(`############ src ${name} = ${srcPropertyValue}${existsOnDest ? ', exists on dest' : ''}, is object? ${srcPropertyIsObject}`); | ||
if (existsOnDest) { | ||
const destPropertyValue = dest[name]; | ||
if (deep && srcPropertyIsObject && destPropertyValue && typeof destPropertyValue === 'object') { | ||
mergeWithHistory(srcPropertyValue, destPropertyValue, replace, deep); | ||
} else if (replace) { | ||
dest[name] = srcPropertyIsObject ? copy(srcPropertyValue, true) : srcPropertyValue; | ||
} | ||
} else { | ||
dest[name] = srcPropertyIsObject ? copy(srcPropertyValue, true) : srcPropertyValue; | ||
} | ||
} else { | ||
to[name] = fromPropIsObject ? merge(fromProp, {}) : fromProp; | ||
} | ||
return dest; | ||
} | ||
return to; | ||
} | ||
return mergeWithHistory(from, to); | ||
} | ||
/** | ||
* Copies the enumerable properties of the given object into a new object. Executes a deep copy if the given deep flag | ||
* is true, otherwise only does a shallow copy. Returns the new copy of the original object. | ||
* @param {Object} object - the object from which to copy enumerable properties into a new object | ||
* @param {boolean|undefined} [deep] - Executes a deep copy if the given deep flag is true, otherwise only does a shallow copy | ||
*/ | ||
function copy(object, deep) { | ||
const history = new WeakMap(); | ||
function copyWithHistory(src, dest) { | ||
if (history.has(src)) { | ||
return history.get(src); | ||
} | ||
// Remember this copy in case the same source appears again within its own graph | ||
history.set(src, dest); | ||
const names = Object.getOwnPropertyNames(src); | ||
for (let i = 0; i < names.length; ++i) { | ||
const name = names[i]; | ||
const property = src[name]; | ||
const propertyIsObject = property && typeof property === 'object'; | ||
dest[name] = deep && propertyIsObject ? copyWithHistory(property, {}) : property; | ||
} | ||
return dest; | ||
} | ||
return copyWithHistory(object, {}); | ||
} |
{ | ||
"name": "core-functions", | ||
"version": "2.0.5", | ||
"version": "2.0.6", | ||
"description": "Core functions, utilities and classes for working with Node/JavaScript primitives and built-in objects, including strings, booleans, Promises, base 64, Arrays, Objects, standard AppErrors, etc.", | ||
@@ -5,0 +5,0 @@ "author": "Byron du Preez", |
@@ -1,2 +0,2 @@ | ||
# core-functions v2.0.5 | ||
# core-functions v2.0.6 | ||
@@ -106,2 +106,7 @@ Core functions, utilities and classes for working with Node/JavaScript primitives and built-in objects, including | ||
### 2.0.6 | ||
- Change to `objects.js`: | ||
- Improved `merge` function to handle circular, non-Directed Acyclic Graphs of objects | ||
- Added a `copy` function also able to handle non-DAGs | ||
### 2.0.5 | ||
@@ -108,0 +113,0 @@ - Change to `objects.js`: |
@@ -100,3 +100,111 @@ 'use strict'; | ||
// check that functions get merged in too | ||
function a1() {} | ||
function a2() {} | ||
function b() {} | ||
function c() {} | ||
const from5 = {a: a2, b: b, c: c, z: 'Z2'}; | ||
const to5 = {a: a1, z: 'Z1'}; | ||
const expected5 = {a: a1, b: b, c: c, z: 'Z1'}; | ||
t.deepEqual(Objects.merge(from5, to5, false, false), expected5, 'deep merge without replace must have all functions of to5 and only extra functions of from5'); | ||
t.equal(to5.a, a1, 'to5.a must be function a1'); | ||
t.equal(to5.b, b, 'to5.b must be function b'); | ||
t.equal(to5.c, c, 'to5.c must be function c'); | ||
t.equal(to5.z, 'Z1', 'to5.z must be Z1'); | ||
function x() {} | ||
const to6 = {a: a1, x: x, y: 'y1', z: 'Z1'}; | ||
const expected6 = {a: a2, b: b, c: c, x: x, y: 'y1', z: 'Z2'}; | ||
t.deepEqual(Objects.merge(from5, to6, true, false), expected6, 'deep merge with replace must have all functions of from5 and only extra functions of to6'); | ||
t.equal(to6.a, a2, 'to6.a must be function a2'); | ||
t.equal(to6.x, x, 'to6.x must be function x'); | ||
t.equal(to6.z, 'Z2', 'to5.z must be Z2'); | ||
const from7 = {a: a2, b: b, c: c, z: 'Z2'}; | ||
const to7 = {a: a1, x: x, y: 'y1', z: 'Z1'}; | ||
const expected7 = {a: a2, b: b, c: c, x: x, y: 'y1', z: 'Z2'}; | ||
t.deepEqual(Objects.merge(from7, to7, true, true), expected7, 'deep merge with replace must have all functions of from7 and only extra functions of to7'); | ||
function d() {} | ||
const from8 = {o: {a: a2, b: b}, c: c, z: 'Z2'}; | ||
const to8 = {o: {a: a1, x: x, y: 'y1'}, d: d, z: 'Z1'}; | ||
const expected8 = {o: {a: a1, b: b, x: x, y: 'y1'}, c: c, d: d, z: 'Z1'}; | ||
t.deepEqual(Objects.merge(from8, to8, false, true), expected8, 'deep merge without replace must have all functions of to8 and only extra functions of from8'); | ||
const from9 = {o: {a: a2, b: b}, c: c, z: 'Z2'}; | ||
const to9 = {o: {a: a1, x: x, y: 'y1'}, d: d, z: 'Z1'}; | ||
const expected9 = {o: {a: a2, b: b, x: x, y: 'y1'}, c: c, d: d, z: 'Z2'}; | ||
t.deepEqual(Objects.merge(from9, to9, true, true), expected9, 'deep merge with replace must have all functions of from9 and only extra functions of to9'); | ||
const from10 = {o: {a: a2, b: b}, c: c, z: 'Z2'}; | ||
const to10 = {o: {a: a1, x: x, y: 'y1'}, d: d, z: 'Z1'}; | ||
const expected10 = {o: {a: a2, b: b}, c: c, d: d, z: 'Z2'}; | ||
t.deepEqual(Objects.merge(from10, to10, true, false), expected10, 'shallow merge with replace must have all of from10 and only extra top-level properties of to10'); | ||
const from11 = {o: {a: a2, b: b}, c: c, z: 'Z2'}; | ||
const to11 = {o: {a: a1, x: x, y: 'y1'}, d: d, z: 'Z1'}; | ||
const expected11 = {o: {a: a1, x: x, y: 'y1'}, c: c, d: d, z: 'Z1'}; | ||
t.deepEqual(Objects.merge(from11, to11, false, false), expected11, 'shallow merge with replace must have all of to11 and only extra top-level properties of from11'); | ||
// Create infinite loops (non-DAGs) | ||
const o3 = {o: {a: a1, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'}; | ||
o3.o.o3Again = o3; | ||
o3.o.p.o3Again = o3; | ||
const c3 = {o: {a: a2, x: 'X2', p: {b: b, y: 'Y2'}}, c: c, z: 'Z2'}; | ||
c3.o.o3Again = c3; | ||
c3.o.p.o3Again = c3; | ||
Objects.merge(o3, c3, false, true); | ||
//EEK t.deepEqual fails with "Maximum call stack size exceeded" | ||
//t.deepEqual(c3, o3, 'deep copy must be deep equal to o3'); | ||
t.deepEqual(Object.getOwnPropertyNames(c3).sort(), Object.getOwnPropertyNames(o3).sort(), 'deep merge circular - c3 must have same names as o3'); | ||
t.deepEqual(Object.getOwnPropertyNames(c3.o).sort(), Object.getOwnPropertyNames(o3.o).sort(), 'deep merge circular - c3.o must have same names as o3.o'); | ||
t.deepEqual(Object.getOwnPropertyNames(c3.o.p).sort(), Object.getOwnPropertyNames(o3.o.p).sort(), 'deep merge circular - c3.o.p must have same names as o3.o.p'); | ||
t.notEqual(c3, o3, 'deep merge circular - c3 must not be o3'); | ||
t.notEqual(c3.o, o3.o, 'deep merge circular - c3.o must not be o3.o'); | ||
t.notEqual(c3.o.p, o3.o.p, 'deep merge circular - c3.o.p must not be o3.o.p'); | ||
t.equal(c3, c3.o.o3Again, 'deep merge circular - c3 must be c3.o.o3Again'); | ||
t.equal(c3, c3.o.p.o3Again, 'deep merge circular - c3 must be c3.o.p.o3Again'); | ||
t.end(); | ||
}); | ||
test('copy', t => { | ||
function a() {} | ||
function b() {} | ||
function c() {} | ||
//function d() {} | ||
const o1 = {o: {a: a, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'}; | ||
const c1 = Objects.copy(o1, false); | ||
t.deepEqual(c1, o1, 'shallow copy circular - c1 must be deep equal to o1'); | ||
t.notEqual(c1, o1, 'shallow copy circular - c1 must not be o1'); | ||
t.equal(c1.o, o1.o, 'shallow copy circular - c1.o must be o1.o'); | ||
t.equal(c1.o.p, o1.o.p, 'shallow copy circular - c1.o.p must be o1.o.p'); | ||
const o2 = {o: {a: a, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'}; | ||
const c2 = Objects.copy(o2, true); | ||
t.deepEqual(c2, o2, 'deep copy circular - c2 must be deep equal to o2'); | ||
t.notEqual(c2, o2, 'deep copy circular - c2 must not be o2'); | ||
t.notEqual(c2.o, o2.o, 'deep copy circular - c2.o must not be o2.o'); | ||
t.notEqual(c2.o.p, o2.o.p, 'deep copy circular - c2.o.p must not be o2.o.p'); | ||
// Create infinite loops (non-DAGs) | ||
const o3 = {o: {a: a, x: 'X', p: {b: b, y: 'Y'}}, c: c, z: 'Z'}; | ||
o3.o.o3Again = o3; | ||
o3.o.p.o3Again = o3; | ||
const c3 = Objects.copy(o3, true); | ||
//EEK t.deepEqual fails with "Maximum call stack size exceeded" | ||
//t.deepEqual(c3, o3, 'deep copy must be deep equal to o3'); | ||
t.deepEqual(Object.getOwnPropertyNames(c3), Object.getOwnPropertyNames(o3), 'deep copy circular - c3 must have same names as o3'); | ||
t.deepEqual(Object.getOwnPropertyNames(c3.o), Object.getOwnPropertyNames(o3.o), 'deep copy circular - c3.o must have same names as o3.o'); | ||
t.deepEqual(Object.getOwnPropertyNames(c3.o.p), Object.getOwnPropertyNames(o3.o.p), 'deep copy circular - c3.o.p must have same names as o3.o.p'); | ||
t.notEqual(c3, o3, 'deep copy circular - c3 must not be o3'); | ||
t.notEqual(c3.o, o3.o, 'deep copy circular - c3.o must not be o3.o'); | ||
t.notEqual(c3.o.p, o3.o.p, 'deep copy circular - c3.o.p must not be o3.o.p'); | ||
t.equal(c3, c3.o.o3Again, 'deep copy circular - c3 must be c3.o.o3Again'); | ||
t.equal(c3, c3.o.p.o3Again, 'deep copy circular - c3 must be c3.o.p.o3Again'); | ||
t.end(); | ||
}); |
{ | ||
"name": "core-functions-tests", | ||
"version": "2.0.5", | ||
"version": "2.0.6", | ||
"author": "Byron du Preez", | ||
@@ -13,5 +13,5 @@ "license": "Apache-2.0", | ||
"devDependencies": { | ||
"tape": "^4.6.2" | ||
"tape": "^4.6.3" | ||
}, | ||
"repository": "https://github.com/byron-dupreez/core-functions" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
180398
3546
159