@@ -23,70 +23,72 @@ import jsonpointer._
23
23
import cats .implicits ._
24
24
25
25
import scala .annotation .tailrec
26
+ import cats .data .Chain
26
27
27
28
class JsonDiff [Json ](diffArray : Boolean , rememberOld : Boolean )(implicit J : Jsony [Json ], Lcs : Lcs [Json ])
28
29
extends Diff [Json , JsonPatch [Json ]] {
29
30
def diff (json1 : Json , json2 : Json ): JsonPatch [Json ] =
30
- JsonPatch (diff(json1, json2, Pointer .Root ))
31
+ JsonPatch (diff(json1, json2, Pointer .Root ).toList )
31
32
32
- private def diff (json1 : Json , json2 : Json , pointer : Pointer ): List [Operation [Json ]] =
33
+ private def diff (json1 : Json , json2 : Json , pointer : Pointer ): Chain [Operation [Json ]] =
33
34
if (json1 === json2)
34
35
// if they are equal, this one is easy...
35
- Nil
36
+ Chain .empty
36
37
else
37
38
(json1, json2) match {
38
39
case (JsObject (fields1), JsObject (fields2)) => fieldsDiff(fields1.toList, fields2.toList, pointer)
39
40
case (JsArray (arr1), JsArray (arr2)) if diffArray => arraysDiff(arr1.toList, arr2.toList, pointer)
40
- case (_, _) => List (Replace (pointer, json2, if (rememberOld) Some (json1) else None ))
41
+ case (_, _) => Chain .one (Replace (pointer, json2, if (rememberOld) Some (json1) else None ))
41
42
}
42
43
43
44
private def fieldsDiff (fields1 : List [(String , Json )],
44
45
fields2 : List [(String , Json )],
45
- path : Pointer ): List [Operation [Json ]] = {
46
+ path : Pointer ): Chain [Operation [Json ]] = {
46
47
// sort fields by name in both objects
47
48
val sorted1 = fields1.sortBy(_._1)
48
49
val sorted2 = fields2.sortBy(_._1)
49
50
@ tailrec
50
51
def associate (fields1 : List [(String , Json )],
51
52
fields2 : List [(String , Json )],
52
- acc : List [(Option [(String , Json )], Option [(String , Json )])])
53
- : List [(Option [(String , Json )], Option [(String , Json )])] = (fields1, fields2) match {
53
+ acc : Chain [(Option [(String , Json )], Option [(String , Json )])])
54
+ : Chain [(Option [(String , Json )], Option [(String , Json )])] = (fields1, fields2) match {
54
55
case (f1 :: t1, f2 :: t2) if f1._1 == f2._1 =>
55
56
// same name, associate both
56
- associate(t1, t2, ( Some (f1), Some (f2)) :: acc )
57
+ associate(t1, t2, acc.prepend(( Some (f1), Some (f2))) )
57
58
case (f1 :: t1, f2 :: _) if f1._1 < f2._1 =>
58
59
// the first field is not present in the second object
59
- associate(t1, fields2, ( Some (f1), None ) :: acc )
60
+ associate(t1, fields2, acc.prepend(( Some (f1), None )) )
60
61
case (_ :: _, f2 :: t2) =>
61
62
// the second field is not present in the first object
62
- associate(fields1, t2, ( None , Some (f2)) :: acc )
63
+ associate(fields1, t2, acc.prepend(( None , Some (f2))) )
63
64
case (_, Nil ) =>
64
- fields1.map(Some (_) -> None ) ::: acc
65
+ Chain .fromSeq( fields1.map(Some (_) -> None )) ++ acc
65
66
case (Nil , _) =>
66
- fields2.map(None -> Some (_)) ::: acc
67
+ Chain .fromSeq( fields2.map(None -> Some (_))) ++ acc
67
68
}
69
+
68
70
@ tailrec
69
71
def fields (fs : List [(Option [(String , Json )], Option [(String , Json )])],
70
- acc : List [Operation [Json ]]): List [Operation [Json ]] = fs match {
72
+ acc : Chain [Operation [Json ]]): Chain [Operation [Json ]] = fs match {
71
73
case (Some (f1), Some (f2)) :: tl if f1 == f2 =>
72
74
// all right, nothing changed
73
75
fields(tl, acc)
74
76
case (Some (f1), Some (f2)) :: tl =>
75
77
// same field name, different values
76
- fields(tl, diff(f1._2, f2._2, path / f1._1) ::: acc)
78
+ fields(tl, diff(f1._2, f2._2, path / f1._1) ++ acc)
77
79
case (Some (f1), None ) :: tl =>
78
80
// the field was deleted
79
- fields(tl, Remove [Json ](path / f1._1, if (rememberOld) Some (f1._2) else None ) :: acc )
81
+ fields(tl, acc.prepend( Remove [Json ](path / f1._1, if (rememberOld) Some (f1._2) else None )) )
80
82
case (None , Some (f2)) :: tl =>
81
83
// the field was added
82
- fields(tl, Add (path / f2._1, f2._2) :: acc )
84
+ fields(tl, acc.prepend( Add (path / f2._1, f2._2)) )
83
85
case _ =>
84
86
acc
85
87
}
86
- fields(associate(sorted1, sorted2, Nil ), Nil )
88
+ fields(associate(sorted1, sorted2, Chain .empty).toList, Chain .empty )
87
89
}
88
90
89
- private def arraysDiff (arr1 : List [Json ], arr2 : List [Json ], path : Pointer ): List [Operation [Json ]] = {
91
+ private def arraysDiff (arr1 : List [Json ], arr2 : List [Json ], path : Pointer ): Chain [Operation [Json ]] = {
90
92
// get the longest common subsequence in the array
91
93
val lcs = Lcs .lcs(arr1, arr2)
92
94
@@ -104,15 +106,16 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
104
106
105
107
// add a bunch of values to an array starting at the specified index
106
108
@ tailrec
107
- def add (arr : List [Json ], idx : Int , acc : List [Operation [Json ]]): List [Operation [Json ]] = arr match {
108
- case v :: tl => add(tl, idx + 1 , Add (path / idx, v) :: acc )
109
- case Nil => acc.reverse
109
+ def add (arr : List [Json ], idx : Int , acc : Chain [Operation [Json ]]): Chain [Operation [Json ]] = arr match {
110
+ case v :: tl => add(tl, idx + 1 , acc.append( Add (path / idx, v)) )
111
+ case Nil => acc
110
112
}
111
113
112
114
// remove a bunch of array elements starting by the last one in the range
113
- def remove (from : Int , until : Int , shift : Int , arr : List [Json ]): List [Operation [Json ]] =
114
- (for (idx <- until to from by - 1 )
115
- yield Remove [Json ](path / idx, if (rememberOld) Some (arr(idx - shift)) else None )).toList
115
+ def remove (from : Int , until : Int , shift : Int , arr : List [Json ]): Chain [Operation [Json ]] =
116
+ Chain .fromSeq(
117
+ for (idx <- until to from by - 1 )
118
+ yield Remove [Json ](path / idx, if (rememberOld) Some (arr(idx - shift)) else None ))
116
119
117
120
// now iterate over the first array to computes what was added, what was removed and what was modified
118
121
@ tailrec
@@ -123,8 +126,8 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
123
126
shift1 : Int , // current index shift in the first array (due to elements being add or removed)
124
127
idx2 : Int , // current index in the second array
125
128
lcs : List [(Int , Int )], // the list of remaining matching indices
126
- acc : List [Operation [Json ]] // the already accumulated result
127
- ): List [Operation [Json ]] = (arr1, arr2) match {
129
+ acc : Chain [Operation [Json ]] // the already accumulated result
130
+ ): Chain [Operation [Json ]] = (arr1, arr2) match {
128
131
case (_ :: tl1, _) if isCommon1(idx1, lcs) =>
129
132
// all values in arr2 were added until the index of common value
130
133
val until = lcs.head._2
@@ -134,7 +137,7 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
134
137
shift1 + until - idx2,
135
138
until + 1 ,
136
139
lcs.tail,
137
- add(arr2.take(until - idx2), idx1 + shift1, Nil ) reverse_:: : acc )
140
+ acc ++ add(arr2.take(until - idx2), idx1 + shift1, Chain .empty) )
138
141
case (_, _ :: tl2) if isCommon2(idx2, lcs) =>
139
142
// all values in arr1 were removed until the index of common value
140
143
val until = lcs.head._1
@@ -144,18 +147,18 @@ class JsonDiff[Json](diffArray: Boolean, rememberOld: Boolean)(implicit J: Jsony
144
147
shift1 - (until - idx1),
145
148
idx2 + 1 ,
146
149
lcs.tail,
147
- remove(idx1 + shift1, until - 1 + shift1, idx1 + shift1, arr1) reverse_:: : acc )
150
+ acc ++ remove(idx1 + shift1, until - 1 + shift1, idx1 + shift1, arr1))
148
151
case (v1 :: tl1, v2 :: tl2) =>
149
152
// values are different, recursively compute the diff of these values
150
- loop(tl1, tl2, idx1 + 1 , shift1, idx2 + 1 , lcs, diff(v1, v2, path / (idx1 + shift1)) reverse_:: : acc )
153
+ loop(tl1, tl2, idx1 + 1 , shift1, idx2 + 1 , lcs, acc ++ diff(v1, v2, path / (idx1 + shift1)))
151
154
case (_, Nil ) =>
152
155
// all subsequent values in arr1 were removed
153
- remove(idx1 + shift1, idx1 + arr1.size - 1 + shift1, idx1 + shift1, arr1) reverse_:: : acc
156
+ acc ++ remove(idx1 + shift1, idx1 + arr1.size - 1 + shift1, idx1 + shift1, arr1)
154
157
case (Nil , _) =>
155
158
// all subsequent value in arr2 were added
156
- arr2.map(Add (path / " -" , _)) reverse_:: : acc
159
+ acc ++ Chain .fromSeq( arr2.map(Add (path / " -" , _)))
157
160
}
158
161
159
- loop(arr1, arr2, 0 , 0 , 0 , lcs, Nil ).reverse
162
+ loop(arr1, arr2, 0 , 0 , 0 , lcs, Chain .empty)
160
163
}
161
164
}
0 commit comments