Como se le preguntó, aquí hay una función de comparación recursiva de objetos. Y un poquito más. Suponiendo que el uso principal de dicha función es la inspección de objetos, tengo algo que decir. La comparación profunda completa es una mala idea cuando algunas diferencias son irrelevantes. Por ejemplo, la comparación ciega profunda en las afirmaciones TDD hace que las pruebas sean innecesariamente frágiles. Por esa razón, me gustaría presentar una diferencia parcial mucho más valiosa . Es un análogo recursivo de una contribución previa a este hilo. Ignora las claves no presentes en un
var bdiff = (a, b) =>
_.reduce(a, (res, val, key) =>
res.concat((_.isPlainObject(val) || _.isArray(val)) && b
? bdiff(val, b[key]).map(x => key + '.' + x)
: (!b || val != b[key] ? [key] : [])),
[]);
BDiff permite verificar los valores esperados mientras tolera otras propiedades, que es exactamente lo que desea para la inspección automática . Esto permite construir todo tipo de afirmaciones avanzadas. Por ejemplo:
var diff = bdiff(expected, actual);
// all expected properties match
console.assert(diff.length == 0, "Objects differ", diff, expected, actual);
// controlled inequality
console.assert(diff.length < 3, "Too many differences", diff, expected, actual);
Volviendo a la solución completa. Construir un diferencial tradicional completo con bdiff es trivial:
function diff(a, b) {
var u = bdiff(a, b), v = bdiff(b, a);
return u.filter(x=>!v.includes(x)).map(x=>' < ' + x)
.concat(u.filter(x=>v.includes(x)).map(x=>' | ' + x))
.concat(v.filter(x=>!u.includes(x)).map(x=>' > ' + x));
};
Ejecutar la función anterior en dos objetos complejos generará algo similar a esto:
[
" < components.0.components.1.components.1.isNew",
" < components.0.cryptoKey",
" | components.0.components.2.components.2.components.2.FFT.min",
" | components.0.components.2.components.2.components.2.FFT.max",
" > components.0.components.1.components.1.merkleTree",
" > components.0.components.2.components.2.components.2.merkleTree",
" > components.0.components.3.FFTResult"
]
Finalmente, para tener una idea de cómo difieren los valores, es posible que queramos evaluar directamente () la salida de diferencias. Para eso, necesitamos una versión más fea de bdiff que genere rutas sintácticamente correctas:
// provides syntactically correct output
var bdiff = (a, b) =>
_.reduce(a, (res, val, key) =>
res.concat((_.isPlainObject(val) || _.isArray(val)) && b
? bdiff(val, b[key]).map(x =>
key + (key.trim ? '':']') + (x.search(/^\d/)? '.':'[') + x)
: (!b || val != b[key] ? [key + (key.trim ? '':']')] : [])),
[]);
// now we can eval output of the diff fuction that we left unchanged
diff(a, b).filter(x=>x[1] == '|').map(x=>[x].concat([a, b].map(y=>((z) =>eval('z.' + x.substr(3))).call(this, y)))));
Eso generará algo similar a esto:
[" | components[0].components[2].components[2].components[2].FFT.min", 0, 3]
[" | components[0].components[2].components[2].components[2].FFT.max", 100, 50]
Licencia MIT;)