Skip to content

Commit

Permalink
feat(join): implements Join methods for interface & primitives
Browse files Browse the repository at this point in the history
Signed-off-by: Alexandre Nicolaie <[email protected]>
  • Loading branch information
xunleii committed Apr 13, 2020
1 parent da76f23 commit 7f7d271
Show file tree
Hide file tree
Showing 10 changed files with 600 additions and 0 deletions.
19 changes: 19 additions & 0 deletions benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,22 @@ func BenchmarkDrop(b *testing.B) {
}
})
}

func BenchmarkJoin(b *testing.B) {
r := rand.New(rand.NewSource(seed))
fullArr := sliceGenerator(sliceSize, r)
leftArr := fullArr[:sliceSize/3*2]
rightArr := fullArr[sliceSize/3*1:]

b.Run("InnerJoinInt64", func(b *testing.B) {
for n := 0; n < b.N; n++ {
JoinInt64(leftArr, rightArr, InnerJoinInt64)
}
})

b.Run("InnerJoin", func(b *testing.B) {
for n := 0; n < b.N; n++ {
Join(leftArr, rightArr, InnerJoin)
}
})
}
1 change: 1 addition & 0 deletions builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Builder interface {
FlattenDeep() Builder
Initial() Builder
Intersect(y interface{}) Builder
Join(rarr interface{}, fnc JoinFnc) Builder
Map(mapFunc interface{}) Builder
Reverse() Builder
Shuffle() Builder
Expand Down
3 changes: 3 additions & 0 deletions chain_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ func (b *chainBuilder) Initial() Builder {
func (b *chainBuilder) Intersect(y interface{}) Builder {
return &chainBuilder{Intersect(b.collection, y)}
}
func (b *chainBuilder) Join(rarr interface{}, fnc JoinFnc) Builder {
return &chainBuilder{Join(b.collection, rarr, fnc)}
}
func (b *chainBuilder) Map(mapFunc interface{}) Builder {
return &chainBuilder{Map(b.collection, mapFunc)}
}
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
4 changes: 4 additions & 0 deletions intersection.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import (
)

// Intersect returns the intersection between two collections.
//
// Deprecated: use Join(x, y, InnerJoin) instead of Intersect, InnerJoin
// implements deduplication mechanism, so verify your code behaviour
// before using it
func Intersect(x interface{}, y interface{}) interface{} {
if !IsCollection(x) {
panic("First parameter must be a collection")
Expand Down
3 changes: 3 additions & 0 deletions intersection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ func TestIntersect(t *testing.T) {
r = Intersect([]string{"foo", "bar", "hello", "bar"}, []string{"foo", "bar"})
is.Equal(r, []string{"foo", "bar"})

r = Intersect([]string{"foo", "bar"}, []string{"foo", "bar", "hello", "bar"})
is.Equal(r, []string{"foo", "bar", "bar"})

}

func TestIntersectString(t *testing.T) {
Expand Down
86 changes: 86 additions & 0 deletions join.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package funk

import (
"reflect"
)

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
}
Loading

0 comments on commit 7f7d271

Please sign in to comment.