Skip to content

Commit 119ed15

Browse files
committed
refactor: enhance MergeObject and MergeArray functions with capacity preallocation and nil checks
1 parent 2a3452b commit 119ed15

2 files changed

Lines changed: 155 additions & 78 deletions

File tree

jsonte/types/array.go

Lines changed: 52 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -196,26 +196,36 @@ func AsArray(obj interface{}) *JsonArray {
196196

197197
// MergeArray merges two JSON arrays into a new JSON array.
198198
func MergeArray(template, parent *JsonArray, keepOverrides bool, path string) *JsonArray {
199-
result := &JsonArray{Value: make([]JsonType, 0, len(template.Value)+len(parent.Value))}
200-
for i, v := range template.Value {
201-
if IsObject(v) {
202-
merge := MergeObject(NewJsonObject(), AsObject(v), keepOverrides, joinArrayPath(path, i))
203-
result.Value = append(result.Value, merge)
204-
} else if IsArray(v) {
205-
merge := MergeArray(NewJsonArray(), AsArray(v), keepOverrides, joinArrayPath(path, i))
206-
result.Value = append(result.Value, merge)
207-
} else {
199+
if template == nil {
200+
template = NewJsonArray()
201+
}
202+
if parent == nil {
203+
parent = NewJsonArray()
204+
}
205+
templateLen := len(template.Value)
206+
result := &JsonArray{Value: make([]JsonType, 0, templateLen+len(parent.Value))}
207+
for _, v := range template.Value {
208+
switch typed := v.(type) {
209+
case *JsonObject:
210+
result.Value = append(result.Value, DeepCopyObject(typed))
211+
case *JsonArray:
212+
result.Value = append(result.Value, DeepCopyArray(typed))
213+
default:
208214
result.Value = append(result.Value, v)
209215
}
210216
}
211217
for i, v := range parent.Value {
212-
if IsObject(v) {
213-
merge := MergeObject(NewJsonObject(), AsObject(v), keepOverrides, joinArrayPath(path, i))
214-
result.Value = append(result.Value, merge)
215-
} else if IsArray(v) {
216-
merge := MergeArray(NewJsonArray(), AsArray(v), keepOverrides, joinArrayPath(path, i))
217-
result.Value = append(result.Value, merge)
218-
} else {
218+
childPath := joinArrayPath(path, i)
219+
switch typed := v.(type) {
220+
case *JsonObject:
221+
result.Value = append(result.Value, MergeObject(NewJsonObjectWithCapacity(typed.Size()), typed, keepOverrides, childPath))
222+
case *JsonArray:
223+
childCapacity := 0
224+
if typed != nil {
225+
childCapacity = len(typed.Value)
226+
}
227+
result.Value = append(result.Value, MergeArray(NewJsonArrayWithCapacity(childCapacity), typed, keepOverrides, childPath))
228+
default:
219229
result.Value = append(result.Value, v)
220230
}
221231
}
@@ -224,19 +234,38 @@ func MergeArray(template, parent *JsonArray, keepOverrides bool, path string) *J
224234

225235
// DeepCopyArray creates a deep copy of the given JSON array.
226236
func DeepCopyArray(object *JsonArray) *JsonArray {
227-
var result = NewJsonArray()
237+
if object == nil {
238+
return NewJsonArray()
239+
}
240+
result := NewJsonArrayWithCapacity(len(object.Value))
228241
for _, v := range object.Value {
229-
if IsObject(v) {
230-
result.Value = append(result.Value, DeepCopyObject(AsObject(v)))
231-
} else if IsArray(v) {
232-
result.Value = append(result.Value, DeepCopyArray(AsArray(v)))
233-
} else {
242+
switch typed := v.(type) {
243+
case *JsonObject:
244+
if typed == nil {
245+
result.Value = append(result.Value, Null)
246+
continue
247+
}
248+
result.Value = append(result.Value, DeepCopyObject(typed))
249+
case *JsonArray:
250+
if typed == nil {
251+
result.Value = append(result.Value, Null)
252+
continue
253+
}
254+
result.Value = append(result.Value, DeepCopyArray(typed))
255+
default:
234256
result.Value = append(result.Value, v)
235257
}
236258
}
237259
return result
238260
}
239261

240262
func NewJsonArray() *JsonArray {
241-
return &JsonArray{Value: make([]JsonType, 0)}
263+
return NewJsonArrayWithCapacity(0)
264+
}
265+
266+
func NewJsonArrayWithCapacity(capacity int) *JsonArray {
267+
if capacity < 0 {
268+
capacity = 0
269+
}
270+
return &JsonArray{Value: make([]JsonType, 0, capacity)}
242271
}

jsonte/types/object.go

Lines changed: 103 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -315,85 +315,133 @@ func IsEqualObject(a, b utils.NavigableMap[string, JsonType]) bool {
315315
// MergeObject merges two JSON objects into a new JSON object.
316316
// If the same value, that is not an object or an array exists in both objects, the value from the second object will be used.
317317
func MergeObject(template, parent *JsonObject, keepOverrides bool, path string) *JsonObject {
318-
result := NewJsonObject()
319-
for _, k := range template.Keys() {
320-
v := template.Get(k)
321-
if IsObject(v) {
322-
result.Put(k, DeepCopyObject(AsObject(v)))
323-
} else if IsArray(v) {
324-
result.Put(k, DeepCopyArray(AsArray(v)))
325-
} else {
326-
result.Put(k, v)
318+
if template == nil {
319+
template = NewJsonObject()
320+
}
321+
if parent == nil {
322+
parent = NewJsonObject()
323+
}
324+
templateCapacity := 0
325+
if template.Value != nil {
326+
templateCapacity = template.Value.Size()
327+
}
328+
parentCapacity := 0
329+
if parent.Value != nil {
330+
parentCapacity = parent.Value.Size()
331+
}
332+
result := NewJsonObjectWithCapacity(templateCapacity + parentCapacity)
333+
for _, key := range template.Keys() {
334+
value := template.Get(key)
335+
switch typed := value.(type) {
336+
case *JsonObject:
337+
result.Put(key, DeepCopyObject(typed))
338+
case *JsonArray:
339+
result.Put(key, DeepCopyArray(typed))
340+
default:
341+
result.Put(key, value)
327342
}
328343
}
329-
skipKeys := make(map[string]struct{})
330-
out:
331-
for _, k := range parent.Keys() {
332-
v := parent.Get(k)
333-
if _, ok := skipKeys[k]; ok {
334-
continue out
344+
skipKeys := make(map[string]struct{}, parentCapacity)
345+
for _, rawKey := range parent.Keys() {
346+
if _, ok := skipKeys[rawKey]; ok {
347+
continue
348+
}
349+
value := parent.Get(rawKey)
350+
isReversedMerge := len(rawKey) > 0 && rawKey[0] == '^'
351+
key := rawKey
352+
if isReversedMerge {
353+
key = rawKey[1:]
335354
}
336-
isReversedMerge := strings.HasPrefix(k, "^")
337-
k = strings.TrimPrefix(k, "^")
338-
if strings.HasPrefix(k, "$") && !IsReservedKey(k) {
339-
trimmedKey := strings.TrimPrefix(k, "$")
355+
if len(key) == 0 {
356+
continue
357+
}
358+
if key[0] == '$' && !IsReservedKey(key) {
359+
trimmedKey := key[1:]
340360
if keepOverrides {
341-
result.Put(k, v)
361+
result.Put(key, value)
342362
} else {
343-
if IsObject(v) {
344-
result.Put(trimmedKey, MergeObject(NewJsonObject(), AsObject(v), keepOverrides, joinObjectPath(path, k)))
345-
} else if IsArray(v) {
346-
result.Put(trimmedKey, MergeArray(NewJsonArray(), AsArray(v), keepOverrides, joinObjectPath(path, k)))
347-
} else {
348-
result.Put(trimmedKey, v)
363+
childPath := joinObjectPath(path, key)
364+
switch typed := value.(type) {
365+
case *JsonObject:
366+
childCapacity := 0
367+
if typed != nil {
368+
childCapacity = typed.Size()
369+
}
370+
result.Put(trimmedKey, MergeObject(NewJsonObjectWithCapacity(childCapacity), typed, keepOverrides, childPath))
371+
case *JsonArray:
372+
childCapacity := 0
373+
if typed != nil {
374+
childCapacity = len(typed.Value)
375+
}
376+
result.Put(trimmedKey, MergeArray(NewJsonArrayWithCapacity(childCapacity), typed, keepOverrides, childPath))
377+
default:
378+
result.Put(trimmedKey, value)
349379
}
350380
}
351381
skipKeys[trimmedKey] = struct{}{}
352-
} else if !template.ContainsKey(k) {
353-
if IsObject(v) {
354-
merge := MergeObject(NewJsonObject(), AsObject(v), keepOverrides, joinObjectPath(path, k))
355-
result.Put(k, merge)
356-
} else if IsArray(v) {
357-
merge := MergeArray(NewJsonArray(), AsArray(v), keepOverrides, joinObjectPath(path, k))
358-
result.Put(k, merge)
382+
continue
383+
}
384+
templateValue, exists := template.TryGet(key)
385+
if !exists {
386+
childPath := joinObjectPath(path, key)
387+
switch typed := value.(type) {
388+
case *JsonObject:
389+
childCapacity := 0
390+
if typed != nil {
391+
childCapacity = typed.Size()
392+
}
393+
result.Put(key, MergeObject(NewJsonObjectWithCapacity(childCapacity), typed, keepOverrides, childPath))
394+
case *JsonArray:
395+
childCapacity := 0
396+
if typed != nil {
397+
childCapacity = len(typed.Value)
398+
}
399+
result.Put(key, MergeArray(NewJsonArrayWithCapacity(childCapacity), typed, keepOverrides, childPath))
400+
default:
401+
result.Put(key, value)
402+
}
403+
continue
404+
}
405+
childPath := joinObjectPath(path, key)
406+
switch typed := value.(type) {
407+
case *JsonObject:
408+
if templateObj, ok := templateValue.(*JsonObject); ok {
409+
result.Put(key, MergeObject(templateObj, typed, keepOverrides, childPath))
359410
} else {
360-
result.Put(k, v)
411+
result.Put(key, value)
361412
}
362-
} else {
363-
if IsObject(v) && IsObject(result.Get(k)) {
364-
merge := MergeObject(AsObject(template.Get(k)), AsObject(v), keepOverrides, joinObjectPath(path, k))
365-
result.Put(k, merge)
366-
} else if IsArray(v) && IsArray(template.Get(k)) {
367-
var merge, v1 *JsonArray
368-
if result.ContainsKey(k) {
369-
v1 = AsArray(result.Get(k))
370-
} else {
371-
v1 = AsArray(template.Get(k))
372-
}
413+
case *JsonArray:
414+
if _, ok := templateValue.(*JsonArray); ok {
415+
base := AsArray(result.Get(key))
373416
if isReversedMerge {
374-
merge = MergeArray(AsArray(v), v1, keepOverrides, joinObjectPath(path, k))
417+
result.Put(key, MergeArray(typed, base, keepOverrides, childPath))
375418
} else {
376-
merge = MergeArray(v1, AsArray(v), keepOverrides, joinObjectPath(path, k))
419+
result.Put(key, MergeArray(base, typed, keepOverrides, childPath))
377420
}
378-
result.Put(k, merge)
379421
} else {
380-
result.Put(k, v)
422+
result.Put(key, value)
381423
}
424+
default:
425+
result.Put(key, value)
382426
}
383427
}
384428
return result
385429
}
386430

387431
// DeepCopyObject creates a deep copy of the given JSON object.
388432
func DeepCopyObject(object *JsonObject) *JsonObject {
389-
result := NewJsonObject()
433+
if object == nil {
434+
return NewJsonObject()
435+
}
436+
result := NewJsonObjectWithCapacity(object.Size())
390437
for _, k := range object.Keys() {
391438
v := object.Get(k)
392-
if IsObject(v) {
393-
result.Put(k, DeepCopyObject(AsObject(v)))
394-
} else if IsArray(v) {
395-
result.Put(k, DeepCopyArray(AsArray(v)))
396-
} else {
439+
switch typed := v.(type) {
440+
case *JsonObject:
441+
result.Put(k, DeepCopyObject(typed))
442+
case *JsonArray:
443+
result.Put(k, DeepCopyArray(typed))
444+
default:
397445
result.Put(k, v)
398446
}
399447
}

0 commit comments

Comments
 (0)