Wie es gefragt wurde, ist hier eine rekursive Objektvergleichsfunktion. Und ein bisschen mehr. Unter der Annahme, dass die primäre Verwendung einer solchen Funktion die Objektinspektion ist, habe ich etwas zu sagen. Ein vollständiger tiefer Vergleich ist eine schlechte Idee, wenn einige Unterschiede irrelevant sind. Zum Beispiel macht ein blinder tiefer Vergleich in TDD-Behauptungen Tests unnötig spröde. Aus diesem Grund möchte ich einen viel wertvolleren Teilunterschied einführen . Es ist ein rekursives Analogon eines früheren Beitrags zu diesem Thread. Schlüssel, die in a nicht vorhanden sind, werden ignoriert
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] : [])),
[]);
Mit BDiff können Sie nach erwarteten Werten suchen und andere Eigenschaften tolerieren. Genau das möchten Sie für die automatische Überprüfung. Dies ermöglicht das Erstellen aller Arten von erweiterten Behauptungen. Zum Beispiel:
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);
Zurück zur vollständigen Lösung. Das Erstellen eines vollständigen traditionellen Diff mit bdiff ist 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));
};
Wenn Sie die obige Funktion für zwei komplexe Objekte ausführen, wird Folgendes ausgegeben:
[
" < 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"
]
Um einen Einblick in die Unterschiede zwischen den Werten zu erhalten, möchten wir möglicherweise die Diff-Ausgabe direkt auswerten () . Dafür benötigen wir eine hässlichere Version von bdiff , die syntaktisch korrekte Pfade ausgibt:
// 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)))));
Das wird etwas Ähnliches ausgeben:
[" | components[0].components[2].components[2].components[2].FFT.min", 0, 3]
[" | components[0].components[2].components[2].components[2].FFT.max", 100, 50]
MIT-Lizenz;)