Skip to content

Commit

Permalink
Merge pull request #10 from azihsoyn/feature/typed_func
Browse files Browse the repository at this point in the history
enable typed func
  • Loading branch information
azihsoyn authored Oct 28, 2016
2 parents eeef859 + 0c05fef commit 885c1b1
Show file tree
Hide file tree
Showing 21 changed files with 184 additions and 128 deletions.
16 changes: 13 additions & 3 deletions distinct.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (g *gollection) Distinct() *gollection {
}
}

func (g *gollection) DistinctBy(f func(v interface{}) interface{}) *gollection {
func (g *gollection) DistinctBy(f interface{}) *gollection {
if g.err != nil {
return &gollection{err: g.err}
}
Expand All @@ -47,12 +47,22 @@ func (g *gollection) DistinctBy(f func(v interface{}) interface{}) *gollection {
}
}

ret := reflect.MakeSlice(sv.Type(), 0, sv.Len())
funcValue := reflect.ValueOf(f)
funcType := funcValue.Type()
if funcType.Kind() != reflect.Func || funcType.NumIn() != 1 || funcType.NumOut() != 1 {
return &gollection{
slice: nil,
err: fmt.Errorf("gollection.DistinctBy called with invalid func. required func(in <T>) out <T> but supplied %v", g.slice),
}
}

resultSliceType := reflect.SliceOf(funcType.In(0))
ret := reflect.MakeSlice(resultSliceType, 0, sv.Len())
m := make(map[interface{}]bool)

for i := 0; i < sv.Len(); i++ {
v := sv.Index(i)
id := f(v.Interface())
id := funcValue.Call([]reflect.Value{v})[0].Interface()
if _, ok := m[id]; !ok {
ret = reflect.Append(ret, v)
m[id] = true
Expand Down
10 changes: 7 additions & 3 deletions distinct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,24 @@ func TestDistinctBy(t *testing.T) {
arr := []string{"aaa", "bb", "c", "ddd", "ee", "f"}
expect := []string{"aaa", "bb", "c"}

res, err := gollection.New(arr).DistinctBy(func(v interface{}) interface{} {
return len(v.(string))
res, err := gollection.New(arr).DistinctBy(func(v string) int {
return len(v)
}).Result()
assert.NoError(err)
assert.Equal(expect, res)
}

func TestDistinctBy_NotSlice(t *testing.T) {
assert := assert.New(t)
_, err := gollection.New("not slice value").DistinctBy(func(v interface{}) interface{} {
return v
}).Result()
assert.Error(err)
}
func TestDistinctBy_NotFunc(t *testing.T) {
assert := assert.New(t)
_, err := gollection.New([]int{}).DistinctBy(0).Result()
assert.Error(err)
}

func TestDistinctBy_HavingError(t *testing.T) {
assert := assert.New(t)
Expand Down
4 changes: 2 additions & 2 deletions examples/distinct/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ func main() {
{ID: 6, Name: "Charles"},
}

res, _ = gollection.New(users).DistinctBy(func(v interface{}) interface{} {
return v.(user).Name
res, _ = gollection.New(users).DistinctBy(func(v user) string {
return v.Name
}).Result()
fmt.Println("origin : ", users)
fmt.Println("ret : ", res)
Expand Down
7 changes: 2 additions & 5 deletions examples/filter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@ import (
func main() {
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

res, _ := gollection.New(arr).Filter(func(v interface{}) bool {
if n, ok := v.(int); ok && n > 5 {
return true
}
return false
res, _ := gollection.New(arr).Filter(func(v int) bool {
return v > 5
}).Result()
fmt.Println("origin : ", arr)
fmt.Println("ret : ", res)
Expand Down
7 changes: 2 additions & 5 deletions examples/flatMap/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,8 @@ func main() {
{6, 7, 8, 9, 10},
}

res, _ := gollection.New(arr).FlatMap(func(v interface{}) interface{} {
if n, ok := v.(int); ok {
return n * 2
}
return 0
res, _ := gollection.New(arr).FlatMap(func(v int) int {
return v * 2
}).Result()
fmt.Println("origin : ", arr)
fmt.Println("ret : ", res)
Expand Down
9 changes: 2 additions & 7 deletions examples/fold/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@ import (
func main() {
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

res, _ := gollection.New(arr).Fold(100, func(v1, v2 interface{}) interface{} {
n1, ok1 := v1.(int)
n2, ok2 := v2.(int)
if ok1 && ok2 {
return n1 + n2
}
return ""
res, _ := gollection.New(arr).Fold(100, func(v1, v2 int) int {
return v1 + v2
}).Result()
fmt.Println("origin : ", arr)
fmt.Println("ret : ", res)
Expand Down
7 changes: 2 additions & 5 deletions examples/map/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@ import (
func main() {
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

res, _ := gollection.New(arr).Map(func(v interface{}) interface{} {
if n, ok := v.(int); ok {
return n * 2
}
return 0
res, _ := gollection.New(arr).Map(func(v int) int {
return v * 2
}).Result()
fmt.Println("origin : ", arr)
fmt.Println("ret : ", res)
Expand Down
9 changes: 2 additions & 7 deletions examples/reduce/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,8 @@ import (
func main() {
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

res, _ := gollection.New(arr).Reduce(func(v1, v2 interface{}) interface{} {
n1, ok1 := v1.(int)
n2, ok2 := v2.(int)
if ok1 && ok2 {
return n1 + n2
}
return ""
res, _ := gollection.New(arr).Reduce(func(v1, v2 int) int {
return v1 + v2
}).Result()
fmt.Println("origin : ", arr)
fmt.Println("ret : ", res)
Expand Down
4 changes: 2 additions & 2 deletions examples/sort/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
func main() {
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1}

res, _ := gollection.New(arr).SortBy(func(v1, v2 interface{}) bool {
return v1.(int) < v2.(int)
res, _ := gollection.New(arr).SortBy(func(v1, v2 int) bool {
return v1 < v2
}).Result()
fmt.Println("origin : ", arr)
fmt.Println("ret : ", res)
Expand Down
16 changes: 13 additions & 3 deletions filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"reflect"
)

func (g *gollection) Filter(f func(v interface{}) bool) *gollection {
func (g *gollection) Filter(f interface{}) *gollection {
if g.err != nil {
return &gollection{err: g.err}
}
Expand All @@ -18,11 +18,21 @@ func (g *gollection) Filter(f func(v interface{}) bool) *gollection {
}
}

ret := reflect.MakeSlice(sv.Type(), 0, sv.Len())
funcValue := reflect.ValueOf(f)
funcType := funcValue.Type()
if funcType.Kind() != reflect.Func || funcType.NumIn() != 1 || funcType.NumOut() != 1 || funcType.Out(0).Kind() != reflect.Bool {
return &gollection{
slice: nil,
err: fmt.Errorf("gollection.Filter called with invalid func. required func(in <T>) bool but supplied %v", g.slice),
}
}

resultSliceType := reflect.SliceOf(funcType.In(0))
ret := reflect.MakeSlice(resultSliceType, 0, sv.Len())

for i := 0; i < sv.Len(); i++ {
v := sv.Index(i)
if f(v.Interface()) {
if funcValue.Call([]reflect.Value{v})[0].Interface().(bool) {
ret = reflect.Append(ret, sv.Index(i))
}
}
Expand Down
13 changes: 7 additions & 6 deletions filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,24 @@ func TestFilter(t *testing.T) {
arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
expect := []int{6, 7, 8, 9, 10}

res, err := gollection.New(arr).Filter(func(v interface{}) bool {
if n, ok := v.(int); ok && n > 5 {
return true
}
return false
res, err := gollection.New(arr).Filter(func(v int) bool {
return v > 5
}).Result()
assert.NoError(err)
assert.Equal(expect, res)
}

func TestFilter_NotSlice(t *testing.T) {
assert := assert.New(t)
_, err := gollection.New("not slice value").Filter(func(v interface{}) bool {
return true
}).Result()
assert.Error(err)
}
func TestFilter_NotFunc(t *testing.T) {
assert := assert.New(t)
_, err := gollection.New([]int{0}).Filter(0).Result()
assert.Error(err)
}

func TestFilter_HavingError(t *testing.T) {
assert := assert.New(t)
Expand Down
29 changes: 21 additions & 8 deletions flat_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"reflect"
)

func (g *gollection) FlatMap(f func(v interface{}) interface{}) *gollection {
func (g *gollection) FlatMap(f interface{}) *gollection {
if g.err != nil {
return &gollection{err: g.err}
}
Expand All @@ -18,9 +18,25 @@ func (g *gollection) FlatMap(f func(v interface{}) interface{}) *gollection {
}
}

// init
retType := reflect.ValueOf(f(nil)).Type()
ret := reflect.MakeSlice(reflect.SliceOf(retType), 0, sv.Len())
currentType := reflect.TypeOf(g.slice).Elem()
if currentType.Kind() != reflect.Slice {
return &gollection{
slice: nil,
err: fmt.Errorf("gollection.FlatMap called with non-slice-of-slice value of type %T", g.slice),
}
}

funcValue := reflect.ValueOf(f)
funcType := funcValue.Type()
if funcType.Kind() != reflect.Func || funcType.NumIn() != 1 || funcType.NumOut() != 1 {
return &gollection{
slice: nil,
err: fmt.Errorf("gollection.FlatMap called with invalid func. required func(in <T>) out <T> but supplied %v", g.slice),
}
}

resultSliceType := reflect.SliceOf(funcType.Out(0))
ret := reflect.MakeSlice(resultSliceType, 0, sv.Len())

// avoid "panic: reflect: call of reflect.Value.Interface on zero Value"
// see https://github.com/azihsoyn/gollection/issues/7
Expand All @@ -33,11 +49,8 @@ func (g *gollection) FlatMap(f func(v interface{}) interface{}) *gollection {
for i := 0; i < sv.Len(); i++ {
v := sv.Index(i).Interface()
svv := reflect.ValueOf(v)
if svv.Kind() != reflect.Slice {
continue
}
for j := 0; j < svv.Len(); j++ {
v := reflect.ValueOf(f(svv.Index(j).Interface()))
v := funcValue.Call([]reflect.Value{svv.Index(j)})[0]
ret = reflect.Append(ret, v)
}
}
Expand Down
33 changes: 22 additions & 11 deletions flat_map_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,27 @@ func TestFlatMap(t *testing.T) {
}
expect := []int{2, 4, 6, 8, 10, 12, 14, 16, 18, 20}

res, err := gollection.New(arr).FlatMap(func(v interface{}) interface{} {
if n, ok := v.(int); ok {
return n * 2
}
return 0
res, err := gollection.New(arr).FlatMap(func(v int) int {
return v * 2
}).Result()
assert.NoError(err)
assert.Equal(expect, res)
}

func TestFlatMap_InterfaceSlice(t *testing.T) {
assert := assert.New(t)
arr := []interface{}{
[]int{1, 2, 3},
"a", "b",
arr := [][]interface{}{
[]interface{}{1, 2, 3},
[]interface{}{"a", "b"},
nil,
}
expect := []int{2, 4, 6}
expect := []interface{}{2, 4, 6, "a", "b"}

res, err := gollection.New(arr).FlatMap(func(v interface{}) interface{} {
if n, ok := v.(int); ok {
return n * 2
}
return 0
return v
}).Result()
assert.NoError(err)
assert.Equal(expect, res)
Expand All @@ -49,13 +46,27 @@ func TestFlatMap_InterfaceSlice(t *testing.T) {
func TestFlatMap_EmptySlice(t *testing.T) {
assert := assert.New(t)
expect := []string{}
res, err := gollection.New([][]int{}).FlatMap(func(v interface{}) interface{} {
res, err := gollection.New([][]int{}).FlatMap(func(v int) string {
return ""
}).Result()
assert.NoError(err)
assert.Equal(expect, res)
}

func TestFlatMap_NotFunc(t *testing.T) {
assert := assert.New(t)
_, err := gollection.New([][]int{}).FlatMap(0).Result()
assert.Error(err)
}

func TestFlatMap_NonSliceOfSlice(t *testing.T) {
assert := assert.New(t)
_, err := gollection.New([]int{}).FlatMap(func(v int) string {
return ""
}).Result()
assert.Error(err)
}

func TestFlatMap_NotSlice(t *testing.T) {
assert := assert.New(t)
_, err := gollection.New("not slice value").FlatMap(func(v interface{}) interface{} {
Expand Down
20 changes: 15 additions & 5 deletions fold.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"reflect"
)

func (g *gollection) Fold(v0 interface{}, f func(v1, v2 interface{}) interface{}) *gollection {
func (g *gollection) Fold(v0 interface{}, f interface{}) *gollection {
if g.err != nil {
return &gollection{err: g.err}
}
Expand All @@ -24,13 +24,23 @@ func (g *gollection) Fold(v0 interface{}, f func(v1, v2 interface{}) interface{}
}
}

v1 := v0
funcValue := reflect.ValueOf(f)
funcType := funcValue.Type()
if funcType.Kind() != reflect.Func || funcType.NumIn() != 2 || funcType.NumOut() != 1 {
return &gollection{
slice: nil,
err: fmt.Errorf("gollection.Fold called with invalid func. required func(in1, in2 <T>) out <T> but supplied %v", g.slice),
}
}

ret := v0
for i := 0; i < sv.Len(); i++ {
v2 := sv.Index(i).Interface()
v1 = f(v1, v2)
v1 := reflect.ValueOf(ret)
v2 := sv.Index(i)
ret = funcValue.Call([]reflect.Value{v1, v2})[0].Interface()
}

return &gollection{
val: v1,
val: ret,
}
}
Loading

0 comments on commit 885c1b1

Please sign in to comment.