File: join.go

package info (click to toggle)
golang-github-thoas-go-funk 0.9.3-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 504 kB
  • sloc: makefile: 10
file content (111 lines) | stat: -rw-r--r-- 2,937 bytes parent folder | download
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()
}