diff --git a/README.md b/README.md index 590955a..76cbed7 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ \_/__/ ``` -# Underscore.go [![GoDoc](https://godoc.org/github.com/ahl5esoft/golang-underscore?status.svg)](https://godoc.org/github.com/ahl5esoft/golang-underscore) [![Go Report Card](https://goreportcard.com/badge/github.com/ahl5esoft/golang-underscore)](https://goreportcard.com/report/github.com/ahl5esoft/golang-underscore) ![Version](https://img.shields.io/badge/version-2.3.0-green.svg) +# Underscore.go [![GoDoc](https://godoc.org/github.com/ahl5esoft/golang-underscore?status.svg)](https://godoc.org/github.com/ahl5esoft/golang-underscore) [![Go Report Card](https://goreportcard.com/badge/github.com/ahl5esoft/golang-underscore)](https://goreportcard.com/report/github.com/ahl5esoft/golang-underscore) ![Version](https://img.shields.io/badge/version-2.4.0-green.svg) like underscore.js and C# LINQ, but for Go ## Installation @@ -57,6 +57,7 @@ like underscore.js and C# LINQ, but for G * [`Sort`](#order), [`SortBy`](#orderBy) * [`Take`](#take) * [`Uniq`](#distinct), [`UniqBy`](#distinctBy) +* [`Value`](#value) * [`Values`](#values) * [`Where`](#where), [`WhereBy`](#whereBy) @@ -526,15 +527,11 @@ __Arguments__ __Examples__ ```go -src := []string{ "a", "b" } -var res map[string]string -Chain(src).Index(func (r string, _ int) string { - return r +src := []string{"a", "b"} +res := make(map[string]string) +Chain(src).Index(func(item string, _ int) string { + return item }).Value(&res) -// or -res := Index(src, func (r string, _ int) string { - return r -}).(map[string]string) // res = map[a:a b:b] ``` @@ -555,10 +552,8 @@ arr := []testModel{ {ID: 3, Name: "b"}, {ID: 4, Name: "b"}, } -var res map[int]testModel +res := make(map[string]testModel) Chain(arr).IndexBy("id").Value(&res) -// or -res := IndexBy(arr, "id").(map[int]testModel) // res = map[1:{{0} 1 a} 2:{{0} 2 a} 3:{{0} 3 b} 4:{{0} 4 b}] ``` @@ -987,6 +982,43 @@ Chain(src).Take(1).Value(&dst) // res = [1] ``` + + +### Value(res interface{}) + +__Arguments__ + +* `res` - array or slice or reflect.Value(array) or reflect.Value(map) + +__Examples__ + +```go +resValue := reflect.New( + reflect.SliceOf( + reflect.TypeOf(1), + ), +) +Chain([]string{"a", "b"}).Map(func(_ string, i int) int { + return i +}).Value(resValue) +// resValue = &[0 1] + +src := []testModel{ + {ID: 1, Name: "a"}, + {ID: 2, Name: "a"}, + {ID: 3, Name: "b"}, + {ID: 4, Name: "b"}, +} +resValue := reflect.New( + reflect.MapOf( + reflect.TypeOf(src[0].Name), + reflect.TypeOf(src), + ), +) +Chain(src).GroupBy("name").Value(resValue) +// res = &map[even:[2 4] odd:[1 3 5]] +``` + ### Values() IEnumerable diff --git a/aggregate_test.go b/aggregate_test.go index d3366ce..a37009a 100644 --- a/aggregate_test.go +++ b/aggregate_test.go @@ -7,30 +7,32 @@ import ( ) func Benchmark_Aggregate(b *testing.B) { - for n := 0; n < b.N; n++ { - total := 0 - Range(1, 100, 1).Aggregate( - func(memo []int, r, _ int) []int { - memo = append(memo, r) - memo = append(memo, -r) - return memo - }, - make([]int, 0), - ).Value(&total) - } -} + b.Run("default", func(b *testing.B) { + for n := 0; n < b.N; n++ { + total := 0 + Range(1, 100, 1).Aggregate( + func(memo []int, r, _ int) []int { + memo = append(memo, r) + memo = append(memo, -r) + return memo + }, + make([]int, 0), + ).Value(&total) + } + }) -func Benchmark_Aggregate_NoValue(b *testing.B) { - for n := 0; n < b.N; n++ { - Range(1, 100, 1).Aggregate( - func(memo []int, r, _ int) []int { - memo = append(memo, r) - memo = append(memo, -r) - return memo - }, - make([]int, 0), - ) - } + b.Run("no value", func(b *testing.B) { + for n := 0; n < b.N; n++ { + Range(1, 100, 1).Aggregate( + func(memo []int, r, _ int) []int { + memo = append(memo, r) + memo = append(memo, -r) + return memo + }, + make([]int, 0), + ) + } + }) } func Test_Aggregate(t *testing.T) { diff --git a/group.go b/group.go index 3674fe7..1e4e640 100644 --- a/group.go +++ b/group.go @@ -1,6 +1,8 @@ package underscore -import "reflect" +import ( + "reflect" +) func (m enumerable) Group(keySelector interface{}) enumerable { return enumerable{ diff --git a/group_test.go b/group_test.go index 8ef4b18..894b852 100644 --- a/group_test.go +++ b/group_test.go @@ -1,6 +1,7 @@ package underscore import ( + "reflect" "testing" "github.com/stretchr/testify/assert" @@ -18,51 +19,90 @@ func Benchmark_Group(b *testing.B) { } } -func Benchmark_Group_New(b *testing.B) { - for n := 0; n < b.N; n++ { - dst := make([]int, 0) - Range(1, benchmarkSize, 1).Group(func(n, _ int) string { +func Test_Group(t *testing.T) { + t.Run("default", func(t *testing.T) { + res := make(map[string][]int) + Chain([]int{1, 2, 3, 4, 5}).Group(func(n, _ int) string { if n%2 == 0 { return "even" } return "odd" - }).Value(&dst) - } -} + }).Value(&res) + assert.EqualValues( + t, + res, + map[string][]int{ + "odd": {1, 3, 5}, + "even": {2, 4}, + }, + ) + }) -func Test_Group(t *testing.T) { - res := make(map[string][]int) - Chain([]int{1, 2, 3, 4, 5}).Group(func(n, _ int) string { - if n%2 == 0 { - return "even" - } - return "odd" - }).Value(&res) - assert.EqualValues( - t, - res, - map[string][]int{ - "odd": {1, 3, 5}, - "even": {2, 4}, - }, - ) + t.Run("reflect.Value", func(t *testing.T) { + resValue := reflect.New( + reflect.MapOf( + reflect.TypeOf(""), + reflect.TypeOf([]int{}), + ), + ) + Chain([]int{1, 2, 3, 4, 5}).Group(func(n, _ int) string { + if n%2 == 0 { + return "even" + } + return "odd" + }).Value(resValue) + assert.EqualValues( + t, + resValue.Interface(), + map[string][]int{ + "odd": {1, 3, 5}, + "even": {2, 4}, + }, + ) + }) } func Test_GroupBy(t *testing.T) { - src := []testModel{ - {ID: 1, Name: "a"}, - {ID: 2, Name: "a"}, - {ID: 3, Name: "b"}, - {ID: 4, Name: "b"}, - } - res := make(map[string][]testModel) - Chain(src).GroupBy("Name").Value(&res) - assert.EqualValues( - t, - res, - map[string][]testModel{ - "a": {src[0], src[1]}, - "b": {src[2], src[3]}, - }, - ) + t.Run("default", func(t *testing.T) { + src := []testModel{ + {ID: 1, Name: "a"}, + {ID: 2, Name: "a"}, + {ID: 3, Name: "b"}, + {ID: 4, Name: "b"}, + } + res := make(map[string][]testModel) + Chain(src).GroupBy("Name").Value(&res) + assert.EqualValues( + t, + res, + map[string][]testModel{ + "a": {src[0], src[1]}, + "b": {src[2], src[3]}, + }, + ) + }) + + t.Run("reflect.Value", func(t *testing.T) { + src := []testModel{ + {ID: 1, Name: "a"}, + {ID: 2, Name: "a"}, + {ID: 3, Name: "b"}, + {ID: 4, Name: "b"}, + } + res := reflect.New( + reflect.MapOf( + reflect.TypeOf(src[0].Name), + reflect.TypeOf(src), + ), + ) + Chain(src).GroupBy("name").Value(res) + assert.EqualValues( + t, + res.Elem().Interface(), + map[string][]testModel{ + "a": {src[0], src[1]}, + "b": {src[2], src[3]}, + }, + ) + }) } diff --git a/map_test.go b/map_test.go index 13cb632..6085f91 100644 --- a/map_test.go +++ b/map_test.go @@ -1,6 +1,8 @@ package underscore import ( + "fmt" + "reflect" "strconv" "testing" @@ -8,7 +10,7 @@ import ( ) func Test_Map(t *testing.T) { - t.Run("ok", func(t *testing.T) { + t.Run("default", func(t *testing.T) { src := []string{"11", "12", "13"} var res []int Chain(src).Map(func(s string, _ int) int { @@ -22,7 +24,7 @@ func Test_Map(t *testing.T) { ) }) - t.Run("ptr", func(t *testing.T) { + t.Run("元素是指针", func(t *testing.T) { src := []string{"11", "12", "13"} var res []*string Chain(src).Map(func(r string, _ int) *string { @@ -34,6 +36,25 @@ func Test_Map(t *testing.T) { []*string{&src[0], &src[1], &src[2]}, ) }) + + t.Run("结果为reflect.Value", func(t *testing.T) { + resValue := reflect.New( + reflect.SliceOf( + reflect.TypeOf(1), + ), + ) + Chain([]string{"a", "b"}).Map(func(_ string, i int) int { + return i + }).Value(resValue) + assert.EqualValues( + t, + resValue.Elem().Interface(), + []int{0, 1}, + ) + fmt.Println(resValue) + + assert.True(t, false) + }) } func Test_MapBy(t *testing.T) { diff --git a/value.go b/value.go index 20c815f..807fef3 100644 --- a/value.go +++ b/value.go @@ -1,9 +1,16 @@ package underscore -import "reflect" +import ( + "reflect" +) func (m enumerable) Value(res interface{}) { - resValue := reflect.ValueOf(res) + var resValue reflect.Value + var ok bool + if resValue, ok = res.(reflect.Value); !ok { + resValue = reflect.ValueOf(res) + } + switch resValue.Elem().Kind() { case reflect.Array, reflect.Slice: m.valueToArrayOrSlice(resValue) @@ -33,7 +40,9 @@ func (m enumerable) valueToArrayOrSlice(resValue reflect.Value) { func (m enumerable) valueToMap(resValue reflect.Value) { iterator := m.GetEnumerator() - mapValue := resValue.Elem() + mapValue := reflect.MakeMap( + resValue.Elem().Type(), + ) for ok := iterator.MoveNext(); ok; ok = iterator.MoveNext() { mapValue.SetMapIndex( iterator.GetKey(),