Skip to content

Commit

Permalink
增加Chain Benchmark
Browse files Browse the repository at this point in the history
IEnumerable增加Group、GroupBy
优化IEnumerable的Distinct、Enumerator、Index、Property、Select、Where
  • Loading branch information
ahl5esoft committed Jun 18, 2019
1 parent 6f2dbcf commit c25f30d
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 154 deletions.
54 changes: 21 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,65 +430,46 @@ Chain2([][]int{

<a name="group" />

### 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]]
```

<a name="groupBy" />

### 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}]]
```

<a name="index" />
Expand Down Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions chain_test.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package underscore

import "testing"

var (
benchmarkSize = 1000000
)
Expand All @@ -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)
}
}
4 changes: 1 addition & 3 deletions distinct.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion enumerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
71 changes: 54 additions & 17 deletions group.go
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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),
}
})
}
61 changes: 29 additions & 32 deletions group_test.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
2 changes: 2 additions & 0 deletions i-enumerable.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 1 addition & 6 deletions index.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}

Expand Down
19 changes: 1 addition & 18 deletions property.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
}
Loading

0 comments on commit c25f30d

Please sign in to comment.