Mam dwa przedmioty: oldObj
i newObj
.
Dane oldObj
zostały użyte do wypełnienia formularza i newObj
są wynikiem zmiany danych użytkownika w tym formularzu i przesłania go.
Oba obiekty są głębokie, tj. mają właściwości, które są obiektami lub tablicami obiektów itp. - mogą mieć głębokość n poziomów, dlatego algorytm różnicy musi być rekurencyjny.
Teraz muszę nie tylko dowiedzieć się, co zostało zmienione (jak dodano / zaktualizowano / usunąć) od oldObj
do newObj
, ale także jak najlepiej to przedstawić.
Do tej pory myślałem o zbudowaniu genericDeepDiffBetweenObjects
metody, która zwróciłaby obiekt na formularzu, {add:{...},upd:{...},del:{...}}
ale potem pomyślałem: ktoś inny musiał tego wcześniej potrzebować.
Więc ... czy ktoś wie o bibliotece lub fragmencie kodu, który to zrobi i może ma jeszcze lepszy sposób reprezentowania różnicy (w sposób, który nadal jest możliwy do serializacji przez JSON)?
Aktualizacja:
Pomyślałem o lepszym sposobie reprezentacji zaktualizowanych danych, używając tej samej struktury obiektu newObj
, ale przekształcając wszystkie wartości właściwości w obiekty w formularzu:
{type: '<update|create|delete>', data: <propertyValue>}
Więc jeśli newObj.prop1 = 'new value'
i oldObj.prop1 = 'old value'
to się ustawireturnObj.prop1 = {type: 'update', data: 'new value'}
Aktualizacja 2:
Robi się naprawdę owłosiony, gdy dochodzimy do właściwości, które są tablicami, ponieważ tablica [1,2,3]
powinna być liczona jako równa [2,3,1]
, co jest dość proste dla tablic typów opartych na wartościach, takich jak string, int & bool, ale naprawdę trudno jest sobie z tym poradzić, jeśli chodzi o tablice typów referencyjnych, takich jak obiekty i tablice.
Przykładowe tablice, które należy uznać za równe:
[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]
Sprawdzenie tego rodzaju głębokiej równości wartości jest nie tylko dość skomplikowane, ale również wymyślenie dobrego sposobu przedstawienia potencjalnych zmian.
źródło
newObj
jest generowany przez odczyt kodu js z formularza w DOM. Istnieje kilka sposobów, aby utrzymać stan i zrobić to o wiele łatwiej, ale chciałbym, aby ćwiczenie to było bezstanowe. Szukam również stanu techniki, aby zobaczyć, jak inni mogliby rozwiązać ten problem, jeśli w rzeczywistości ktokolwiek to zrobił.Odpowiedzi:
Napisałem małą klasę, która robi to, co chcesz, możesz to przetestować tutaj .
Jedyną rzeczą, która różni się od twojej propozycji, jest to, że nie uważam
[1,[{c: 1},2,3],{a:'hey'}] and [{a:'hey'},1,[3,{c: 1},2]]
tego samego, ponieważ uważam, że tablice nie są równe, jeśli kolejność ich elementów nie jest taka sama. Oczywiście można to zmienić w razie potrzeby. Również ten kod można dodatkowo ulepszyć, aby pełnił funkcję argumentu, który zostanie użyty do sformatowania obiektu diff w dowolny sposób na podstawie przekazanych prymitywnych wartości (teraz to zadanie jest wykonywane metodą „CompareValues”).źródło
c
jest tworzony jako,undefined
ale powinien być ciągiem'i am created'
), a poza tym nie robi tego, czego potrzebuję, ponieważ brakuje mu głębokiej wartości porównania, która jest najważniejsza (i złożona / trudna) część. Na marginesie, konstrukcja'array' != typeof(obj)
jest bezużyteczna, ponieważ tablice są obiektami, które są instancjami tablic.{type: ..., data:..}
obiektu. Brakuje przeszukiwania wartości z pierwszej tablicy na sekundę, ale jak wspomniałem w mojej odpowiedzi, nie sądzę, że tablice są równe, jeśli kolejność ich wartości nie jest równa ([1, 2, 3] is not equal to [3, 2, 1]
moim zdaniem).[{key: 'value1'}] and [{key: 'value2'}, {key: 'value3'}]
. Teraz pierwszy obiekt w pierwszej tablicy został zaktualizowany o „wartość1” lub „wartość2”. I to jest prosty przykład, może się skomplikować przy głębokim zagnieżdżaniu. Jeśli chcesz / potrzebujesz głęboko gniazdowania porównania niezależnie od pozycji klucza nie tworzą tablicę obiektów, tworzenie obiektów zagnieżdżonych obiektów, takich jak dla poprzedniego przykładu:{inner: {key: 'value1'}} and {inner: {key: 'value2'}, otherInner: {key: 'value3'}}
.Używając podkreślenia, prosty diff:
Wyniki w częściach,
o1
które odpowiadają, ale z różnymi wartościami wo2
:Byłoby inaczej dla głębokiego różnicy:
Jak wskazał @Juhana w komentarzach, powyższe jest tylko różnicą a -> b i nie jest odwracalne (co oznacza, że dodatkowe właściwości w b zostaną zignorowane). Zamiast tego użyj a -> b -> a:
Zobacz http://jsfiddle.net/drzaus/9g5qoxwj/ dla pełnego przykładu + testy + mixiny
źródło
omit
że będzie to głęboka różnica, ale się myliłem, więc uwzględniono również dla porównania.r[k] = ... : v
wr[k] = ... : {'a':v, 'b':b[k] }
, w ten sposób można zobaczyć dwie wartości.{a:1, b:2}
i{a:1, b:2, c:3}
._.omitBy
zamiast_.omit
.Chciałbym zaoferować rozwiązanie ES6 ... Jest to różnicowanie jednokierunkowe, co oznacza, że zwróci klucze / wartości
o2
, które nie są identyczne z ich odpowiednikami wo1
:źródło
if(o1[key] === o1[key])
linię koleśUncaught SyntaxError: Unexpected token ...
Korzystanie z Lodash:
Nie używam klucza / obiektu / źródła, ale zostawiłem go tam, jeśli chcesz uzyskać do nich dostęp. Porównanie obiektów po prostu uniemożliwia konsoli wydrukowanie różnic w konsoli od elementu najbardziej zewnętrznego do elementu najbardziej wewnętrznego.
Możesz dodać trochę logiki do obsługi tablic. Być może najpierw posortuj tablice. To bardzo elastyczne rozwiązanie.
EDYTOWAĆ
Zmieniono z _.merge na _.mergeWith z powodu aktualizacji lodash. Dziękujemy Aviron za zauważenie zmiany.
źródło
Oto biblioteka JavaScript, której można użyć do znalezienia różnic między dwoma obiektami JavaScript:
Github URL: https://github.com/cosmicanant/recursive-diff
URL Npmjs: https://www.npmjs.com/package/recursive-diff
Możesz używać biblioteki rekursywno-różnicowej w przeglądarce, a także w Node.js. W przypadku przeglądarki wykonaj następujące czynności:
Natomiast w node.js możesz wymagać modułu „recursive-diff” i używać go jak poniżej:
źródło
Obecnie dostępnych jest do tego sporo modułów. Niedawno napisałem moduł, aby to zrobić, ponieważ nie byłem zadowolony z wielu różnych modułów, które znalazłem. To się nazywa
odiff
: https://github.com/Tixit/odiff . Wymieniłem też kilka najpopularniejszych modułów i dlaczego nie były one akceptowane wodiff
pliku Readme , które można przejrzeć, jeśliodiff
nie mają pożądanych właściwości. Oto przykład:źródło
Istnieje moduł npm z ponad 500 000 pobrań tygodniowo: https://www.npmjs.com/package/deep-object-diff
Podoba mi się obiekt jak reprezentacja różnic - szczególnie, gdy łatwo jest zobaczyć strukturę, kiedy jest sformatowana.
źródło
Użyłem tego fragmentu kodu do wykonania opisanego zadania:
dostaniesz nowy obiekt, który połączy wszystkie zmiany między starym obiektem a nowym obiektem z twojego formularza
źródło
$.extend(true,obj1,obj2)
przy użyciu jQuery. To wcale nie jest to, czego potrzebuję. Potrzebuję różnicy między dwoma przedmiotami, a nie ich kombinacji.Opracowałem funkcję o nazwie „CompareValue ()” w JavaScript. zwraca, czy wartość jest taka sama, czy nie. Wywołałem funkcję CompareValue () w pętli for jednego obiektu. możesz uzyskać różnicę dwóch obiektów w diffParams.
źródło
Wiem, że spóźniłem się na przyjęcie, ale potrzebowałem czegoś podobnego, co powyższe odpowiedzi nie pomogły.
Korzystałem z funkcji zegarka $ Angulara do wykrywania zmian w zmiennej. Nie tylko musiałem wiedzieć, czy właściwość zmieniła się w zmiennej, ale chciałem również upewnić się, że zmieniona właściwość nie była tymczasowym polem obliczonym. Innymi słowy, chciałem zignorować pewne właściwości.
Oto kod: https://jsfiddle.net/rv01x6jo/
Oto jak go użyć:
Mam nadzieję, że to komuś pomoże.
źródło
Po prostu używam ramdy, aby rozwiązać ten sam problem, muszę wiedzieć, co zostało zmienione w nowym obiekcie. Oto mój projekt.
wynikiem jest nazwa właściwości i jej status.
źródło
Oto wersja maszynopisu kodu @sbgoran
źródło
Oto zmodyfikowana wersja czegoś znalezionego na gisthub .
źródło
Zmodyfikowałem odpowiedź @ sbgoran, aby wynikowy obiekt diff zawierał tylko zmienione wartości i pomijał te same wartości. Ponadto pokazuje zarówno wartość oryginalną, jak i zaktualizowaną wartość .
źródło
Napisałem już funkcję dla jednego z moich projektów, która porówna obiekt jako opcję użytkownika z jego wewnętrznym klonem. Może również sprawdzać, a nawet zastępować wartości domyślne, jeśli użytkownik wprowadził zły typ danych lub usunął go w czystym javascript.
W IE8 działa w 100%. Testowany pomyślnie.
/ * wynik
źródło
Bardziej rozbudowana i uproszczona funkcja z odpowiedzi sbgorana.
Pozwala to na głębokie skanowanie i znalezienie podobieństwa tablicy.
źródło
Natknąłem się tutaj, próbując znaleźć sposób na uzyskanie różnicy między dwoma przedmiotami. To jest moje rozwiązanie przy użyciu Lodash:
źródło
Przyjąłem powyższą odpowiedź @sbgoran i zmodyfikowałem ją w moim przypadku tak samo, jak potrzebne pytanie, aby traktować tablice jako zestawy (tzn. Kolejność nie jest ważna dla diff)
źródło
Oto rozwiązanie, które:
object
typem)undefined
Najpierw definiujemy interfejs wyniku porównania:
ze szczególnym przypadkiem zmiany, w którym chcemy wiedzieć, jakie są stare i nowe wartości:
Następnie możemy zapewnić
diff
funkcję, która jest zaledwie dwiema pętlami (z rekurencyjnością, jeślideep
jesttrue
):Na przykład wywołanie:
wróci:
i wywołanie tego samego z
deep
trzecim parametrem zwróci:źródło
Ta biblioteka jest jedną z najszybszych do różnic w głębokich obiektach:
https://www.npmjs.com/package/@netilon/differify
źródło