@@ -56,10 +56,23 @@ var (
56
56
ErrNotHandled = errors .New ("cannot compare the reflect.Kind" )
57
57
)
58
58
59
+ const (
60
+ // FLAG_NONE is a placeholder for default Equal behavior. You don't have to
61
+ // pass it to Equal; if you do, it does nothing.
62
+ FLAG_NONE byte = iota
63
+
64
+ // FLAG_IGNORE_SLICE_ORDER causes Equal to ignore slice order and, instead,
65
+ // compare value counts. For example, []{1, 2} and []{2, 1} are equal
66
+ // because each value has the same count. But []{1, 2, 2} and []{1, 2}
67
+ // are not equal because the first slice has two occurrences of value 2.
68
+ FLAG_IGNORE_SLICE_ORDER
69
+ )
70
+
59
71
type cmp struct {
60
72
diff []string
61
73
buff []string
62
74
floatFormat string
75
+ flag map [byte ]bool
63
76
}
64
77
65
78
var errorType = reflect .TypeOf ((* error )(nil )).Elem ()
@@ -74,13 +87,17 @@ var errorType = reflect.TypeOf((*error)(nil)).Elem()
74
87
//
75
88
// When comparing a struct, if a field has the tag `deep:"-"` then it will be
76
89
// ignored.
77
- func Equal (a , b interface {}) []string {
90
+ func Equal (a , b interface {}, flags ... interface {} ) []string {
78
91
aVal := reflect .ValueOf (a )
79
92
bVal := reflect .ValueOf (b )
80
93
c := & cmp {
81
94
diff : []string {},
82
95
buff : []string {},
83
96
floatFormat : fmt .Sprintf ("%%.%df" , FloatPrecision ),
97
+ flag : map [byte ]bool {},
98
+ }
99
+ for i := range flags {
100
+ c .flag [flags [i ].(byte )] = true
84
101
}
85
102
if a == nil && b == nil {
86
103
return nil
@@ -339,29 +356,54 @@ func (c *cmp) equals(a, b reflect.Value, level int) {
339
356
}
340
357
}
341
358
359
+ // Equal if same underlying pointer and same length, this latter handles
360
+ // foo := []int{1, 2, 3, 4}
361
+ // a := foo[0:2] // == {1,2}
362
+ // b := foo[2:4] // == {3,4}
363
+ // a and b are same pointer but different slices (lengths) of the underlying
364
+ // array, so not equal.
342
365
aLen := a .Len ()
343
366
bLen := b .Len ()
344
-
345
367
if a .Pointer () == b .Pointer () && aLen == bLen {
346
368
return
347
369
}
348
370
349
- n := aLen
350
- if bLen > aLen {
351
- n = bLen
352
- }
353
- for i := 0 ; i < n ; i ++ {
354
- c .push (fmt .Sprintf ("slice[%d]" , i ))
355
- if i < aLen && i < bLen {
356
- c .equals (a .Index (i ), b .Index (i ), level + 1 )
357
- } else if i < aLen {
358
- c .saveDiff (a .Index (i ), "<no value>" )
359
- } else {
360
- c .saveDiff ("<no value>" , b .Index (i ))
371
+ if c .flag [FLAG_IGNORE_SLICE_ORDER ] {
372
+ // Compare slices by value and value count; ignore order.
373
+ // Value equality is impliclity established by the maps:
374
+ // any value v1 will hash to the same map value if it's equal
375
+ // to another value v2. Then equality is determiend by value
376
+ // count: presuming v1==v2, then the slics are equal if there
377
+ // are equal numbers of v1 in each slice.
378
+ am := map [interface {}]int {}
379
+ for i := 0 ; i < a .Len (); i ++ {
380
+ am [a .Index (i ).Interface ()] += 1
361
381
}
362
- c .pop ()
363
- if len (c .diff ) >= MaxDiff {
364
- break
382
+ bm := map [interface {}]int {}
383
+ for i := 0 ; i < b .Len (); i ++ {
384
+ bm [b .Index (i ).Interface ()] += 1
385
+ }
386
+ c .cmpMapValueCounts (a , b , am , bm , true ) // a cmp b
387
+ c .cmpMapValueCounts (b , a , bm , am , false ) // b cmp a
388
+ } else {
389
+ // Compare slices by order
390
+ n := aLen
391
+ if bLen > aLen {
392
+ n = bLen
393
+ }
394
+ for i := 0 ; i < n ; i ++ {
395
+ c .push (fmt .Sprintf ("slice[%d]" , i ))
396
+ if i < aLen && i < bLen {
397
+ c .equals (a .Index (i ), b .Index (i ), level + 1 )
398
+ } else if i < aLen {
399
+ c .saveDiff (a .Index (i ), "<no value>" )
400
+ } else {
401
+ c .saveDiff ("<no value>" , b .Index (i ))
402
+ }
403
+ c .pop ()
404
+ if len (c .diff ) >= MaxDiff {
405
+ break
406
+ }
365
407
}
366
408
}
367
409
@@ -435,6 +477,35 @@ func (c *cmp) saveDiff(aval, bval interface{}) {
435
477
}
436
478
}
437
479
480
+ func (c * cmp ) cmpMapValueCounts (a , b reflect.Value , am , bm map [interface {}]int , a2b bool ) {
481
+ for v := range am {
482
+ aCount , _ := am [v ]
483
+ bCount , _ := bm [v ]
484
+
485
+ if aCount != bCount {
486
+ c .push (fmt .Sprintf ("(unordered) slice[]=%v: value count" , v ))
487
+ if a2b {
488
+ c .saveDiff (fmt .Sprintf ("%d" , aCount ), fmt .Sprintf ("%d" , bCount ))
489
+ } else {
490
+ c .saveDiff (fmt .Sprintf ("%d" , bCount ), fmt .Sprintf ("%d" , aCount ))
491
+ }
492
+ c .pop ()
493
+ }
494
+ delete (am , v )
495
+ delete (bm , v )
496
+
497
+ /*else {
498
+ c.push(fmt.Sprintf("(unordered) slice[]=%v: value count", v))
499
+ c.saveDiff(fmt.Sprintf("%d", aCount), "<no value>")
500
+ } else {
501
+ c.saveDiff("<no value>", fmt.Sprintf("%d occurrences", bCount))
502
+ }
503
+ c.pop()
504
+ }
505
+ */
506
+ }
507
+ }
508
+
438
509
func logError (err error ) {
439
510
if LogErrors {
440
511
log .Println (err )
0 commit comments