diff --git a/README.md b/README.md
index e84f248..f0f679e 100644
--- a/README.md
+++ b/README.md
@@ -430,65 +430,46 @@ Chain2([][]int{
-### Group(source, keySelector)
+### Group(keySelector) IEnumerable
+### Group(keySelector) IQuery
__Arguments__
-* `source` - array or map
* `keySelector` - func(element or value, index or key) anyType
-__Return__
-
-* interface{} - map[anyType][](element or value)
-
__Examples__
```go
-src := []int{ 1, 2, 3, 4, 5 }
-var res map[string][]int
-Chain(src).Group(func (n, _ int) string {
- if n % 2 == 0 {
- return "even"
- }
- return "odd"
-}).Value(&res)
-// or
-res := Group(src, func (n, _ int) string {
- if n % 2 == 0 {
+dst := make(map[string][]int)
+Chain2([]int{1, 2, 3, 4, 5}).Group(func(n, _ int) string {
+ if n%2 == 0 {
return "even"
}
return "odd"
-}).(map[string][]int)
-// res = map[odd:[1 3 5] even:[2 4]]
+}).Value(&dst)
+// dst = map[odd:[1 3 5] even:[2 4]]
```
-### GroupBy(source, property)
+### GroupBy(fieldName) IEnumerable
+### GroupBy(fieldName) IQuery
__Arguments__
-* `source` - array or map
-* `property` - property name
-
-__Return__
-
-* interface{} - map[property type][](element or value)
+* `fieldName` - field name
__Examples__
```go
-arr := []testModel{
+dst := make(map[string][]testModel)
+Chain2([]testModel{
{ID: 1, Name: "a"},
{ID: 2, Name: "a"},
{ID: 3, Name: "b"},
{ID: 4, Name: "b"},
-}
-var res map[string][]testModel
-Chain(arr).GroupBy("name").Value(&res)
-// or
-res := GroupBy(arr, "name").(map[string][]testModel)
-// res = map[a:[{{0} 1 a} {{0} 2 a}] b:[{{0} 3 b} {{0} 4 b}]]
+}).GroupBy("Name").Value(&dst)
+// dst = map[a:[{1 a} {2 a}] b:[{3 b} {4 b}]]
```
@@ -1218,6 +1199,13 @@ __Same__
* `FilterBy`
## Release Notes
+~~~
+v1.5.0 (2019-06-18)
+* 增加Chain Benchmark
+* IEnumerable增加Group、GroupBy
+* 优化IEnumerable的Distinct、Enumerator、Index、Property、Select、Where
+~~~
+
~~~
v1.4.0 (2019-06-15)
* Reduce、Take支持IEnumerable
diff --git a/chain_test.go b/chain_test.go
index c02d229..7d56893 100644
--- a/chain_test.go
+++ b/chain_test.go
@@ -1,5 +1,7 @@
package underscore
+import "testing"
+
var (
benchmarkSize = 1000000
)
@@ -14,3 +16,25 @@ type testNestedModel struct {
Age int
}
+
+func Benchmark_Chain(b *testing.B) {
+ for n := 0; n < b.N; n++ {
+ var dst int
+ Range(1, benchmarkSize, 1).Map(func(r, _ int) int {
+ return -r
+ }).Where(func(r, _ int) bool {
+ return r < -20
+ }).First().Value(&dst)
+ }
+}
+
+func Benchmark_Chain_New(b *testing.B) {
+ for n := 0; n < b.N; n++ {
+ var dst int
+ Range2(1, benchmarkSize, 1).Select(func(r, _ int) int {
+ return -r
+ }).Where(func(r, _ int) bool {
+ return r < -20
+ }).First().Value(&dst)
+ }
+}
diff --git a/distinct.go b/distinct.go
index e36d507..22e3c7f 100644
--- a/distinct.go
+++ b/distinct.go
@@ -21,9 +21,7 @@ func (m enumerable) Distinct(selector interface{}) IEnumerable {
for ok = iterator.MoveNext(); ok; ok = iterator.MoveNext() {
valueRV = iterator.GetValue()
keyRV = iterator.GetKey()
- v := getRV(
- selectorRV.Call([]reflect.Value{valueRV, keyRV})[0],
- ).Interface()
+ v := getFuncReturnRV(selectorRV, iterator).Interface()
if _, has := set[v]; !has {
set[v] = true
return
diff --git a/enumerator.go b/enumerator.go
index 7baf903..3f5c7ce 100644
--- a/enumerator.go
+++ b/enumerator.go
@@ -16,7 +16,7 @@ func (m enumerator) GetKey() reflect.Value {
}
func (m enumerator) GetValue() reflect.Value {
- return getRV(m.value)
+ return getRealRV(m.value)
}
func (m *enumerator) MoveNext() (ok bool) {
diff --git a/group.go b/group.go
index f6e2bf1..b22d3e6 100644
--- a/group.go
+++ b/group.go
@@ -1,13 +1,10 @@
package underscore
-import (
- "reflect"
-)
+import "reflect"
-// Group is 分组
-func Group(source, keySelector interface{}) interface{} {
+func (m *query) Group(keySelector interface{}) IQuery {
var groupRV reflect.Value
- each(source, keySelector, func(groupKeyRV, valueRV, _ reflect.Value) bool {
+ each(m.Source, keySelector, func(groupKeyRV, valueRV, _ reflect.Value) bool {
groupValueRT := reflect.SliceOf(valueRV.Type())
if !groupRV.IsValid() {
groupRT := reflect.MapOf(groupKeyRV.Type(), groupValueRT)
@@ -24,27 +21,67 @@ func Group(source, keySelector interface{}) interface{} {
return false
})
if groupRV.IsValid() {
- return groupRV.Interface()
+ m.Source = groupRV.Interface()
}
- return nil
+
+ return m
}
-// GroupBy is 根据某个属性分组
-func GroupBy(source interface{}, property string) interface{} {
+func (m *query) GroupBy(property string) IQuery {
getPropertyRV := PropertyRV(property)
- return Group(source, func(value, _ interface{}) facade {
+ return m.Group(func(value, _ interface{}) facade {
return facade{
getPropertyRV(value),
}
})
}
-func (m *query) Group(keySelector interface{}) IQuery {
- m.Source = Group(m.Source, keySelector)
- return m
+func (m enumerable) Group(keySelector interface{}) enumerable {
+ return enumerable{
+ Enumerator: func() IEnumerator {
+ groupRVs := make(map[interface{}]reflect.Value)
+ iterator := m.GetEnumerator()
+ keySelectorRV := reflect.ValueOf(keySelector)
+ keyRVs := make([]reflect.Value, 0)
+ for ok := iterator.MoveNext(); ok; ok = iterator.MoveNext() {
+ keyRV := getFuncReturnRV(keySelectorRV, iterator)
+ key := keyRV.Interface()
+ groupRV, ok := groupRVs[key]
+ if !ok {
+ groupRV = reflect.MakeSlice(
+ reflect.SliceOf(
+ iterator.GetValue().Type(),
+ ),
+ 0,
+ 0,
+ )
+ keyRVs = append(keyRVs, keyRV)
+ }
+ groupRVs[key] = reflect.Append(
+ groupRV,
+ iterator.GetValue(),
+ )
+ }
+ index := 0
+ return &enumerator{
+ MoveNextFunc: func() (valueRV reflect.Value, keyRV reflect.Value, ok bool) {
+ if ok = index < len(keyRVs); ok {
+ keyRV = keyRVs[index]
+ valueRV = groupRVs[keyRV.Interface()]
+ index++
+ }
+ return
+ },
+ }
+ },
+ }
}
-func (m *query) GroupBy(property string) IQuery {
- m.Source = GroupBy(m.Source, property)
- return m
+func (m enumerable) GroupBy(fieldName string) enumerable {
+ getter := PropertyRV(fieldName)
+ return m.Group(func(value, _ interface{}) facade {
+ return facade{
+ getter(value),
+ }
+ })
}
diff --git a/group_test.go b/group_test.go
index d15c3a3..e89a956 100644
--- a/group_test.go
+++ b/group_test.go
@@ -1,56 +1,53 @@
package underscore
-import (
- "testing"
-)
+import "testing"
-func Test_Group(t *testing.T) {
- dic := Group([]int{1, 2, 3, 4, 5}, func(n, _ int) string {
- if n%2 == 0 {
- return "even"
- }
- return "odd"
- }).(map[string][]int)
- if len(dic["even"]) != 2 {
- t.Error("wrong")
+func Benchmark_Group(b *testing.B) {
+ for n := 0; n < b.N; n++ {
+ dst := make([]int, 0)
+ Range(1, benchmarkSize, 1).Group(func(n, _ int) string {
+ if n%2 == 0 {
+ return "even"
+ }
+ return "odd"
+ }).Value(&dst)
}
}
-func Test_GroupBy(t *testing.T) {
- arr := []testModel{
- {ID: 1, Name: "a"},
- {ID: 2, Name: "a"},
- {ID: 3, Name: "b"},
- {ID: 4, Name: "b"},
- }
- dic := GroupBy(arr, "name").(map[string][]testModel)
- if len(dic) != 2 {
- t.Error("wrong")
+func Benchmark_Group_New(b *testing.B) {
+ for n := 0; n < b.N; n++ {
+ dst := make([]int, 0)
+ Range2(1, benchmarkSize, 1).Group(func(n, _ int) string {
+ if n%2 == 0 {
+ return "even"
+ }
+ return "odd"
+ }).Value(&dst)
}
}
-func Test_Chain_Group(t *testing.T) {
- res := make(map[string][]int)
- Chain([]int{1, 2, 3, 4, 5}).Group(func(n, _ int) string {
+func Test_Group(t *testing.T) {
+ dst := make(map[string][]int)
+ Chain2([]int{1, 2, 3, 4, 5}).Group(func(n, _ int) string {
if n%2 == 0 {
return "even"
}
return "odd"
- }).Value(&res)
- if len(res["even"]) != 2 {
+ }).Value(&dst)
+ if len(dst["even"]) != 2 {
t.Error("wrong")
}
}
-func Test_Chain_GroupBy(t *testing.T) {
- res := make(map[string][]testModel)
- Chain([]testModel{
+func Test_GroupBy(t *testing.T) {
+ dst := make(map[string][]testModel)
+ Chain2([]testModel{
{ID: 1, Name: "a"},
{ID: 2, Name: "a"},
{ID: 3, Name: "b"},
{ID: 4, Name: "b"},
- }).GroupBy("Name").Value(&res)
- if len(res) != 2 {
+ }).GroupBy("Name").Value(&dst)
+ if len(dst) != 2 {
t.Error("wrong")
}
}
diff --git a/i-enumerable.go b/i-enumerable.go
index 2d39a10..c698b6d 100644
--- a/i-enumerable.go
+++ b/i-enumerable.go
@@ -18,6 +18,8 @@ type IEnumerable interface {
FindIndexBy(dict map[string]interface{}) int
First() IEnumerable
GetEnumerator() IEnumerator
+ Group(keySelector interface{}) enumerable
+ GroupBy(fieldName string) enumerable
Index(keySelector interface{}) IEnumerable
IndexBy(fieldName string) IEnumerable
Keys() IEnumerable
diff --git a/index.go b/index.go
index cbd470b..017b9ad 100644
--- a/index.go
+++ b/index.go
@@ -50,12 +50,7 @@ func (m enumerable) Index(keySelector interface{}) IEnumerable {
return &enumerator{
MoveNextFunc: func() (valueRV reflect.Value, keyRV reflect.Value, ok bool) {
if ok = iterator.MoveNext(); ok {
- keyRV = getRV(
- keySelectorRV.Call([]reflect.Value{
- iterator.GetValue(),
- iterator.GetKey(),
- })[0],
- )
+ keyRV = getFuncReturnRV(keySelectorRV, iterator)
valueRV = iterator.GetValue()
}
diff --git a/property.go b/property.go
index b2d32a8..28497ae 100644
--- a/property.go
+++ b/property.go
@@ -20,7 +20,7 @@ func Property(name string) func(interface{}) interface{} {
func PropertyRV(name string) GetProeprtyRVFunc {
var getter GetProeprtyRVFunc
getter = func(item interface{}) reflect.Value {
- itemRV := getRV(item)
+ itemRV := getRealRV(item)
itemRT := itemRV.Type()
for i := 0; i < itemRT.NumField(); i++ {
field := itemRT.Field(i)
@@ -42,20 +42,3 @@ func PropertyRV(name string) GetProeprtyRVFunc {
}
return getter
}
-
-func getRV(v interface{}) reflect.Value {
- rv := reflect.ValueOf(v)
- if rv.Type() == rtOfRV {
- rv = v.(reflect.Value)
- }
-
- if rv.Kind() == reflect.Ptr {
- rv = rv.Elem()
- }
-
- if rv.Type() == facadeRT {
- rv = rv.Interface().(facade).Real
- }
-
- return rv
-}
diff --git a/select.go b/select.go
index dad895e..e7ecf03 100644
--- a/select.go
+++ b/select.go
@@ -5,18 +5,13 @@ import "reflect"
func (m enumerable) Select(selector interface{}) IEnumerable {
return enumerable{
Enumerator: func() IEnumerator {
- index := 0
iterator := m.GetEnumerator()
selectorRV := reflect.ValueOf(selector)
return &enumerator{
MoveNextFunc: func() (valueRV reflect.Value, keyRV reflect.Value, ok bool) {
if ok = iterator.MoveNext(); ok {
- valueRV = selectorRV.Call([]reflect.Value{
- iterator.GetValue(),
- iterator.GetKey(),
- })[0]
- keyRV = reflect.ValueOf(index)
- index++
+ keyRV = iterator.GetKey()
+ valueRV = getFuncReturnRV(selectorRV, iterator)
}
return
diff --git a/util.go b/util.go
index 38aa216..762e495 100644
--- a/util.go
+++ b/util.go
@@ -1,40 +1,29 @@
package underscore
-import (
- "reflect"
-)
+import "reflect"
-// ToRealValue is 将反射值转为真实类型的值
-func ToRealValue(rv reflect.Value) interface{} {
- var value interface{}
- switch rv.Kind() {
- case reflect.Bool:
- value = rv.Bool()
- break
- case reflect.Float32, reflect.Float64:
- value = rv.Float()
- break
- case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
- value = rv.Int()
- break
- case reflect.String:
- value = rv.String()
- break
- case reflect.Struct:
- value = rv.Interface()
- break
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- value = rv.Uint()
- break
- case reflect.Ptr:
- return ToRealValue(
- reflect.Indirect(rv),
- )
- default:
- if !rv.IsNil() {
- value = rv.Interface()
- }
- break
+func getRealRV(v interface{}) reflect.Value {
+ rv := reflect.ValueOf(v)
+ if rv.Type() == rtOfRV {
+ rv = v.(reflect.Value)
}
- return value
+
+ if rv.Kind() == reflect.Ptr {
+ rv = rv.Elem()
+ }
+
+ if rv.Type() == facadeRT {
+ rv = rv.Interface().(facade).Real
+ }
+
+ return rv
+}
+
+func getFuncReturnRV(selectorRV reflect.Value, enumerator IEnumerator) reflect.Value {
+ return getRealRV(
+ selectorRV.Call([]reflect.Value{
+ enumerator.GetValue(),
+ enumerator.GetKey(),
+ })[0],
+ )
}
diff --git a/where.go b/where.go
index fe7adb5..1d610f1 100644
--- a/where.go
+++ b/where.go
@@ -24,8 +24,7 @@ func (m enumerable) Where(predicate interface{}) IEnumerable {
for ok = iterator.MoveNext(); ok; ok = iterator.MoveNext() {
valueRV = iterator.GetValue()
keyRV = iterator.GetKey()
- returnRVs := predicateRV.Call([]reflect.Value{valueRV, keyRV})
- if returnRVs[0].Bool() {
+ if predicateRV.Call([]reflect.Value{valueRV, keyRV})[0].Bool() {
return
}
}