github.com/integration-system/go-cmp@v0.0.0-20190131081942-ac5582987a2f/cmp/internal/value/sort.go (about) 1 // Copyright 2017, The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE.md file. 4 5 package value 6 7 import ( 8 "fmt" 9 "math" 10 "reflect" 11 "sort" 12 ) 13 14 // SortKeys sorts a list of map keys, deduplicating keys if necessary. 15 // The type of each value must be comparable. 16 func SortKeys(vs []reflect.Value) []reflect.Value { 17 if len(vs) == 0 { 18 return vs 19 } 20 21 // Sort the map keys. 22 sort.Slice(vs, func(i, j int) bool { return isLess(vs[i], vs[j]) }) 23 24 // Deduplicate keys (fails for NaNs). 25 vs2 := vs[:1] 26 for _, v := range vs[1:] { 27 if isLess(vs2[len(vs2)-1], v) { 28 vs2 = append(vs2, v) 29 } 30 } 31 return vs2 32 } 33 34 // isLess is a generic function for sorting arbitrary map keys. 35 // The inputs must be of the same type and must be comparable. 36 func isLess(x, y reflect.Value) bool { 37 switch x.Type().Kind() { 38 case reflect.Bool: 39 return !x.Bool() && y.Bool() 40 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 41 return x.Int() < y.Int() 42 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 43 return x.Uint() < y.Uint() 44 case reflect.Float32, reflect.Float64: 45 fx, fy := x.Float(), y.Float() 46 return fx < fy || math.IsNaN(fx) && !math.IsNaN(fy) 47 case reflect.Complex64, reflect.Complex128: 48 cx, cy := x.Complex(), y.Complex() 49 rx, ix, ry, iy := real(cx), imag(cx), real(cy), imag(cy) 50 if rx == ry || (math.IsNaN(rx) && math.IsNaN(ry)) { 51 return ix < iy || math.IsNaN(ix) && !math.IsNaN(iy) 52 } 53 return rx < ry || math.IsNaN(rx) && !math.IsNaN(ry) 54 case reflect.Ptr, reflect.UnsafePointer, reflect.Chan: 55 return x.Pointer() < y.Pointer() 56 case reflect.String: 57 return x.String() < y.String() 58 case reflect.Array: 59 for i := 0; i < x.Len(); i++ { 60 if isLess(x.Index(i), y.Index(i)) { 61 return true 62 } 63 if isLess(y.Index(i), x.Index(i)) { 64 return false 65 } 66 } 67 return false 68 case reflect.Struct: 69 for i := 0; i < x.NumField(); i++ { 70 if isLess(x.Field(i), y.Field(i)) { 71 return true 72 } 73 if isLess(y.Field(i), x.Field(i)) { 74 return false 75 } 76 } 77 return false 78 case reflect.Interface: 79 vx, vy := x.Elem(), y.Elem() 80 if !vx.IsValid() || !vy.IsValid() { 81 return !vx.IsValid() && vy.IsValid() 82 } 83 tx, ty := vx.Type(), vy.Type() 84 if tx == ty { 85 return isLess(x.Elem(), y.Elem()) 86 } 87 if tx.Kind() != ty.Kind() { 88 return vx.Kind() < vy.Kind() 89 } 90 if tx.String() != ty.String() { 91 return tx.String() < ty.String() 92 } 93 if tx.PkgPath() != ty.PkgPath() { 94 return tx.PkgPath() < ty.PkgPath() 95 } 96 // This can happen in rare situations, so we fallback to just comparing 97 // the unique pointer for a reflect.Type. This guarantees deterministic 98 // ordering within a program, but it is obviously not stable. 99 return reflect.ValueOf(vx.Type()).Pointer() < reflect.ValueOf(vy.Type()).Pointer() 100 default: 101 // Must be Func, Map, or Slice; which are not comparable. 102 panic(fmt.Sprintf("%T is not comparable", x.Type())) 103 } 104 }