-
Notifications
You must be signed in to change notification settings - Fork 271
/
join.go
111 lines (95 loc) · 2.87 KB
/
join.go
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()
}