1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
|
package funk
import (
"reflect"
"strings"
)
type JoinFnc func(lx, rx reflect.Value) reflect.Value
// Join combines two collections using the given join method.
func Join(larr, rarr interface{}, fnc JoinFnc) interface{} {
if !IsCollection(larr) {
panic("First parameter must be a collection")
}
if !IsCollection(rarr) {
panic("Second parameter must be a collection")
}
lvalue := reflect.ValueOf(larr)
rvalue := reflect.ValueOf(rarr)
if NotEqual(lvalue.Type(), rvalue.Type()) {
panic("Parameters must have the same type")
}
return fnc(lvalue, rvalue).Interface()
}
// InnerJoin finds and returns matching data from two collections.
func InnerJoin(lx, rx reflect.Value) reflect.Value {
result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), 0, lx.Len()+rx.Len())
rhash := hashSlice(rx)
lhash := make(map[interface{}]struct{}, lx.Len())
for i := 0; i < lx.Len(); i++ {
v := lx.Index(i)
_, ok := rhash[v.Interface()]
_, alreadyExists := lhash[v.Interface()]
if ok && !alreadyExists {
lhash[v.Interface()] = struct{}{}
result = reflect.Append(result, v)
}
}
return result
}
// OuterJoin finds and returns dissimilar data from two collections.
func OuterJoin(lx, rx reflect.Value) reflect.Value {
ljoin := LeftJoin(lx, rx)
rjoin := RightJoin(lx, rx)
result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), ljoin.Len()+rjoin.Len(), ljoin.Len()+rjoin.Len())
for i := 0; i < ljoin.Len(); i++ {
result.Index(i).Set(ljoin.Index(i))
}
for i := 0; i < rjoin.Len(); i++ {
result.Index(ljoin.Len() + i).Set(rjoin.Index(i))
}
return result
}
// LeftJoin finds and returns dissimilar data from the first collection (left).
func LeftJoin(lx, rx reflect.Value) reflect.Value {
result := reflect.MakeSlice(reflect.SliceOf(lx.Type().Elem()), 0, lx.Len())
rhash := hashSlice(rx)
for i := 0; i < lx.Len(); i++ {
v := lx.Index(i)
_, ok := rhash[v.Interface()]
if !ok {
result = reflect.Append(result, v)
}
}
return result
}
// LeftJoin finds and returns dissimilar data from the second collection (right).
func RightJoin(lx, rx reflect.Value) reflect.Value { return LeftJoin(rx, lx) }
func hashSlice(arr reflect.Value) map[interface{}]struct{} {
hash := map[interface{}]struct{}{}
for i := 0; i < arr.Len(); i++ {
v := arr.Index(i).Interface()
hash[v] = struct{}{}
}
return hash
}
// StringerJoin joins an array of elements which implement the `String() string` function.
// Direct copy of strings.Join() with a few tweaks.
func StringerJoin(elems []interface{ String() string }, sep string) string {
switch len(elems) {
case 0:
return ""
case 1:
return elems[0].String()
}
n := len(sep) * (len(elems) - 1)
for i := 0; i < len(elems); i++ {
n += len(elems[i].String())
}
var b strings.Builder
b.Grow(n)
b.WriteString(elems[0].String())
for _, s := range elems[1:] {
b.WriteString(sep)
b.WriteString(s.String())
}
return b.String()
}
|