tpl/collections: Add collections.SymDiff

Fixes #5410
This commit is contained in:
Bjørn Erik Pedersen
2018-11-06 13:04:11 +01:00
parent b8b8436fcc
commit 488776b649
5 changed files with 204 additions and 18 deletions

View File

@@ -14,10 +14,11 @@
package collections
import (
"errors"
"fmt"
"reflect"
"time"
"github.com/pkg/errors"
)
var (
@@ -59,6 +60,47 @@ func normalize(v reflect.Value) interface{} {
return v.Interface()
}
// collects identities from the slices in seqs into a set. Numeric values are normalized,
// pointers unwrapped.
func collectIdentities(seqs ...interface{}) (map[interface{}]bool, error) {
seen := make(map[interface{}]bool)
for _, seq := range seqs {
v := reflect.ValueOf(seq)
switch v.Kind() {
case reflect.Array, reflect.Slice:
for i := 0; i < v.Len(); i++ {
ev, _ := indirectInterface(v.Index(i))
if !ev.Type().Comparable() {
return nil, errors.New("elements must be comparable")
}
seen[normalize(ev)] = true
}
default:
return nil, fmt.Errorf("arguments must be slices or arrays")
}
}
return seen, nil
}
// We have some different numeric and string types that we try to behave like
// they were the same.
func convertValue(v reflect.Value, to reflect.Type) (reflect.Value, error) {
if v.Type().AssignableTo(to) {
return v, nil
}
switch kind := to.Kind(); {
case kind == reflect.String:
s, err := toString(v)
return reflect.ValueOf(s), err
case isNumber(kind):
return convertNumber(v, kind)
default:
return reflect.Value{}, errors.Errorf("%s is not assignable to %s", v.Type(), to)
}
}
// There are potential overflows in this function, but the downconversion of
// int64 etc. into int8 etc. is coming from the synthetic unit tests for Union etc.
// TODO(bep) We should consider normalizing the slices to int64 etc.