Skip to content

Commit 4b141de

Browse files
rootroot
authored andcommitted
merge process array
1 parent 7ffe2c2 commit 4b141de

File tree

2 files changed

+95
-30
lines changed

2 files changed

+95
-30
lines changed

jsonpatch.go

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -384,36 +384,49 @@ func handleValues(av, bv interface{}, p string, patch []Operation) ([]Operation,
384384
return patch, nil
385385
}
386386

387+
// https://github.com/mattbaird/jsonpatch/pull/4
388+
// compareArray generates remove and add operations for `av` and `bv`.
387389
func compareArray(av, bv []interface{}, p string) []Operation {
388390
retval := []Operation{}
389-
// var err error
390-
for i, v := range av {
391-
found := false
392-
for _, v2 := range bv {
393-
if reflect.DeepEqual(v, v2) {
394-
found = true
395-
break
396-
}
397-
}
398-
if !found {
399-
retval = append([]Operation{NewPatch("remove", makePath(p, i), nil)}, retval...)
400-
}
391+
392+
// Find elements that need to be removed
393+
processArray(av, bv, func(i int, value interface{}) {
394+
retval = append(retval, NewPatch("remove", makePath(p, i), nil))
395+
})
396+
reversed := make([]Operation, len(retval))
397+
for i := 0; i < len(retval); i++ {
398+
reversed[len(retval)-1-i] = retval[i]
401399
}
400+
retval = reversed
401+
// Find elements that need to be added.
402+
// NOTE we pass in `bv` then `av` so that processArray can find the missing elements.
403+
processArray(bv, av, func(i int, value interface{}) {
404+
retval = append(retval, NewPatch("add", makePath(p, i), value))
405+
})
402406

403-
for i, v := range bv {
404-
found := false
405-
for _, v2 := range av {
407+
return retval
408+
}
409+
410+
// processArray processes `av` and `bv` calling `applyOp` whenever a value is absent.
411+
// It keeps track of which indexes have already had `applyOp` called for and automatically skips them so you can process duplicate objects correctly.
412+
func processArray(av, bv []interface{}, applyOp func(i int, value interface{})) {
413+
foundIndexes := make(map[int]struct{}, len(av))
414+
reverseFoundIndexes := make(map[int]struct{}, len(av))
415+
for i, v := range av {
416+
for i2, v2 := range bv {
417+
if _, ok := reverseFoundIndexes[i2]; ok {
418+
// We already found this index.
419+
continue
420+
}
406421
if reflect.DeepEqual(v, v2) {
407-
found = true
422+
// Mark this index as found since it matches exactly.
423+
foundIndexes[i] = struct{}{}
424+
reverseFoundIndexes[i2] = struct{}{}
408425
break
409426
}
410427
}
411-
412-
newElementButEqualToAnExistingOne := found && i >= len(av)
413-
if !found || newElementButEqualToAnExistingOne {
414-
retval = append([]Operation{NewPatch("add", makePath(p, i), v)}, retval...)
428+
if _, ok := foundIndexes[i]; !ok {
429+
applyOp(i, v)
415430
}
416431
}
417-
418-
return retval
419432
}

jsonpatch_subarray_test.go

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,77 @@
11
package jsonpatch
22

33
import (
4-
"log"
4+
"encoding/json"
5+
"reflect"
56
"testing"
67

78
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
810
)
911

10-
var subArray1_current = `{"created":1739943104628936621,"updated":0,"index":"test","path":"test","data":{"subFields":[{"one":"one","two":"two","three":3}]}}`
11-
var subArray1_target = `{"created":1739943104628936621,"updated":1739942662496282458,"index":"test","path":"test","data":{"subFields":[{"one":"one","two":"two","three":3},{"one":"one","two":"two","three":3}]}}`
12+
var subArray1_current = `{"created":1739943104628936621,"updated":0,"index":"test","path":"test","data":{"subFields":[{"one":"P","two":"two","three":3}]}}`
13+
var subArray1_target = `{"created":1739943104628936621,"updated":1739942662496282458,"index":"test","path":"test","data":{"subFields":[{"one":"P","two":"two","three":3},{"one":"B","two":"two","three":3}]}}`
1214

13-
var subArray2_current = `{"created":1739943104628936621,"updated":1739942662496282458,"index":"test","path":"test","data":{"subFields":[{"one":"one","two":"two","three":3},{"one":"one","two":"two","three":3}]}}`
14-
var subArray2_target = `{"created":1739943104628936621,"updated":1739942662496282459,"index":"test","path":"test","data":{"subFields":[{"one":"one","two":"two","three":3},{"one":"one","two":"two","three":3},{"one":"one","two":"two","three":3}]}}`
15+
var subArray2_current = `{"created":1739943104628936621,"updated":1739942662496282458,"index":"test","path":"test","data":{"subFields":[{"one":"P","two":"two","three":3},{"one":"B","two":"two","three":3}]}}`
16+
var subArray2_target = `{"created":1739943104628936621,"updated":1739942662496282459,"index":"test","path":"test","data":{"subFields":[{"one":"P","two":"two","three":3},{"one":"B","two":"two","three":3},{"one":"P","two":"two","three":3}]}}`
17+
18+
var subArray3_current = `{"created":1739943104628936621,"updated":1739942662496282459,"index":"test","path":"test","data":{"subFields":[{"one":"P","two":"two","three":3},{"one":"B","two":"two","three":3},{"one":"P","two":"two","three":3}]}}`
19+
var subArray3_target = `{"created":1739943104628936621,"updated":1739942662496282460,"index":"test","path":"test","data":{"subFields":[{"one":"P","two":"two","three":3},{"one":"B","two":"two","three":3},{"one":"P","two":"two","three":3},{"one":"B","two":"two","three":3}]}}`
20+
21+
var subArray4_current = `{"created":1739943104628936621,"updated":1739942662496282460,"index":"test","path":"test","data":{"subFields":[{"one":"P","two":"two","three":3},{"one":"B","two":"two","three":3},{"one":"P","two":"two","three":3},{"one":"B","two":"two","three":3}]}}`
22+
var subArray4_target = `{"created":1739943104628936621,"updated":1739942662496282461,"index":"test","path":"test","data":{"subFields":[{"one":"P","two":"two","three":3},{"one":"B","two":"two","three":3},{"one":"P","two":"two","three":3},{"one":"B","two":"two","three":3},{"one":"P","two":"two","three":3}]}}`
23+
24+
var subArray5_current = `{"created":1739954651885800613,"updated":1739954651890302236,"index":"game","path":"","data":{"id":"18258f8986f08d85","deck":1739954651885767932,"started":1739954651885767932,"ended":0,"burn":false,"burning":0,"overriden":0,"overrider":"","voided":0,"voider":"","edited":0,"editor":"","status":"ongoing","cards":[{"suit":1,"value":10,"owner":"P","show":false},{"suit":1,"value":10,"owner":"B","show":false},{"suit":1,"value":10,"owner":"P","show":false},{"suit":1,"value":10,"owner":"B","show":false}],"tally":{"player":0,"banker":0},"previousCards":null,"result":{"status":"","playerPair":false,"bankerPair":false,"natural8":false,"natural9":false,"lucky6_2":false,"lucky6_3":false},"fakeCards":false}}`
25+
var subArray5__target = `{"created":1739954651885800613,"updated":1739954651891192258,"index":"game","path":"","data":{"id":"18258f8986f08d85","deck":1739954651885767932,"started":1739954651885767932,"ended":0,"burn":false,"burning":0,"overriden":0,"overrider":"","voided":0,"voider":"","edited":0,"editor":"","status":"ongoing","cards":[{"suit":1,"value":10,"owner":"P","show":true},{"suit":1,"value":10,"owner":"B","show":true},{"suit":1,"value":10,"owner":"P","show":true},{"suit":1,"value":10,"owner":"B","show":true},{"suit":1,"value":10,"owner":"P","show":false}],"tally":{"player":0,"banker":0},"previousCards":null,"result":{"status":"","playerPair":false,"bankerPair":false,"natural8":false,"natural9":false,"lucky6_2":false,"lucky6_3":false},"fakeCards":false}}`
26+
27+
type Card struct {
28+
Suit int `json:"suit"`
29+
Value int `json:"value"`
30+
Owner string `json:"owner"`
31+
Show bool `json:"show"`
32+
}
33+
34+
type GameData struct {
35+
Cards []Card `json:"cards"`
36+
}
37+
38+
type Game struct {
39+
Data GameData `json:"data"`
40+
}
1541

1642
func TestSubfieldArray(t *testing.T) {
1743
patch, e := CreatePatch([]byte(subArray1_current), []byte(subArray1_target))
1844
assert.NoError(t, e)
19-
assert.Equal(t, len(patch), 2, "the patch should update 2 fields")
45+
assert.Equal(t, 2, len(patch), "the patch should update 2 fields")
2046

2147
patch, e = CreatePatch([]byte(subArray2_current), []byte(subArray2_target))
2248
assert.NoError(t, e)
23-
log.Println("OP", patch)
24-
assert.Equal(t, len(patch), 2, "the patch should update 2 fields")
49+
assert.Equal(t, 2, len(patch), "the patch should update 2 fields")
50+
51+
patch, e = CreatePatch([]byte(subArray3_current), []byte(subArray3_target))
52+
assert.NoError(t, e)
53+
assert.Equal(t, 2, len(patch), "the patch should update 2 fields")
54+
55+
patch, e = CreatePatch([]byte(subArray4_current), []byte(subArray4_target))
56+
assert.NoError(t, e)
57+
assert.Equal(t, 2, len(patch), "the patch should update 2 fields")
58+
59+
patch, e = CreatePatch([]byte(subArray5_current), []byte(subArray5__target))
60+
assert.NoError(t, e)
61+
// log.Println("OP", patch)
62+
assert.Equal(t, 8, len(patch), "the patch should update 2 fields")
63+
64+
_patch, err := json.Marshal(patch)
65+
require.NoError(t, err)
66+
obj, err := DecodePatch([]byte(_patch))
67+
require.NoError(t, err)
68+
patched, err := obj.Apply([]byte(subArray5_current))
69+
require.NoError(t, err)
70+
71+
var _target = Game{}
72+
json.Unmarshal([]byte(subArray5__target), &_target)
73+
var _patched = Game{}
74+
json.Unmarshal([]byte(patched), &_patched)
75+
76+
require.True(t, reflect.DeepEqual(_target, _patched))
2577
}

0 commit comments

Comments
 (0)