Jak zostało zadane, oto funkcja porównywania obiektów rekurencyjnych. I trochę więcej. Zakładając, że podstawowym zastosowaniem takiej funkcji jest kontrola obiektów, mam coś do powiedzenia. Całkowite dokładne porównanie jest złym pomysłem, gdy pewne różnice są nieistotne. Na przykład ślepe głębokie porównanie w stwierdzeniach TDD sprawia, że testy stają się niepotrzebne. Z tego powodu chciałbym wprowadzić o wiele bardziej wartościowy różnicowy fragment . Jest to rekurencyjny odpowiednik poprzedniego wkładu do tego wątku. Ignoruje klucze nie występujące w A
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 umożliwia sprawdzanie oczekiwanych wartości przy jednoczesnym tolerowaniu innych właściwości, co jest dokładnie tym, czego chcesz do automatycznej kontroli. Pozwala to na budowanie wszelkiego rodzaju zaawansowanych twierdzeń. Na przykład:
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);
Wracając do kompletnego rozwiązania. Budowanie pełnego tradycyjnego diff z bdiff jest banalne:
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));
};
Uruchomienie powyższej funkcji na dwóch złożonych obiektach spowoduje wyświetlenie czegoś podobnego do tego:
[
" < 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"
]
Na koniec, aby rzucić okiem na różnice między wartościami, możemy chcieć bezpośrednio ewaluować () wyjście diff. W tym celu potrzebujemy brzydszej wersji bdiff, która wyświetla poprawne składniowo ścieżki:
// 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)))));
To da wynik podobny do tego:
[" | components[0].components[2].components[2].components[2].FFT.min", 0, 3]
[" | components[0].components[2].components[2].components[2].FFT.max", 100, 50]
Licencja MIT;)