feat(Go-Tool):
2020/12/16: 添加YAML和结构体转换工具
This commit is contained in:
@@ -3,19 +3,11 @@ module Go-Tool
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 // indirect
|
||||
github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible // indirect
|
||||
github.com/ahmetb/go-linq v2.0.0-rc0+incompatible
|
||||
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 // indirect
|
||||
github.com/ahmetb/go-linq v3.0.0+incompatible
|
||||
github.com/go-ini/ini v1.57.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/json-iterator/go v1.1.10
|
||||
github.com/kardianos/govendor v1.0.9 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/smartystreets/goconvey v1.6.4 // indirect
|
||||
github.com/stretchr/testify v1.5.1 // indirect
|
||||
golang.org/x/sys v0.0.0-20200428200454-593003d681fa // indirect
|
||||
golang.org/x/tools v0.0.0-20200429213335-127c98bd7927 // indirect
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2 // indirect
|
||||
gopkg.in/ini.v1 v1.62.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
@@ -1,65 +1,45 @@
|
||||
github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 h1:xGHheKK44eC6K0u5X+DZW/fRaR1LnDdqPHMZMWx5fv8=
|
||||
github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ=
|
||||
github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible h1:8q+6K5jJ2RCTD7v7VxMfuUdHyU22GZi+qMY0hpLTnVc=
|
||||
github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible/go.mod h1:IBvGwXyZVvXuN8KIbGCK53k+fzCKOmAdYhobMdJXOck=
|
||||
github.com/ahmetb/go-linq v2.0.0-rc0+incompatible h1:H8yA1M8T+1/pyPRUwSOyc30MFrtiP9vgJ2s+lgdaQlM=
|
||||
github.com/ahmetb/go-linq v2.0.0-rc0+incompatible/go.mod h1:PFffvbdbtw+QTB0WKRP0cNht7vnCfnGlEpak/DVg5cY=
|
||||
github.com/ahmetb/go-linq v3.0.0+incompatible h1:qQkjjOXKrKOTy83X8OpRmnKflXKQIL/mC/gMVVDMhOA=
|
||||
github.com/ahmetb/go-linq v3.0.0+incompatible/go.mod h1:PFffvbdbtw+QTB0WKRP0cNht7vnCfnGlEpak/DVg5cY=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA=
|
||||
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU=
|
||||
github.com/go-ini/ini v1.57.0 h1:Qwzj3wZQW+Plax5Ntj+GYe07DfGj1OH+aL1nMTMaNow=
|
||||
github.com/go-ini/ini v1.57.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/kardianos/govendor v1.0.9 h1:WOH3FcVI9eOgnIZYg96iwUwrL4eOVx+aQ66oyX2R8Yc=
|
||||
github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200428200454-593003d681fa h1:yMbJOvnfYkO1dSAviTu/ZguZWLBTXx4xE3LYrxUCCiA=
|
||||
golang.org/x/sys v0.0.0-20200428200454-593003d681fa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200429213335-127c98bd7927 h1:qunaQbmBN1l/xZt8mr6MLr8G/IOFiMbXvxYbGVqKlyw=
|
||||
golang.org/x/tools v0.0.0-20200429213335-127c98bd7927/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
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/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
|
||||
2020/12/15:添加前缀树工具,实现前缀搜索和字符串搜索功能
|
||||
|
||||
2020/12/16: 添加YAML和结构体转换工具
|
||||
|
||||
# mod vendor模式加载包
|
||||
通过go mod的方式加载的github上面的包会有报红的问题,但是包本身是可以运行的,这样就是会有一个问题,如果你想要点击去看方法的内容,没办法做到
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* @Author : huangzj
|
||||
* @Time : 2020/12/16 11:56
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v2"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
//把文件中的yaml结构转换成结构体
|
||||
func GetYamlToStructByFile(fileName string, object interface{}) error {
|
||||
yamlFile, err := ioutil.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = yaml.Unmarshal(yamlFile, object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//通过字符串的Yaml转换成结构体
|
||||
func GetYamlToStructByString(content string, object interface{}) error {
|
||||
err := yaml.Unmarshal([]byte(content), object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//通过字符串的Yaml转换成map
|
||||
func GetYamlToMap(content string) (error, map[interface{}]interface{}) {
|
||||
m := make(map[interface{}]interface{}, 0)
|
||||
err := yaml.Unmarshal([]byte(content), m)
|
||||
if err != nil {
|
||||
return err, m
|
||||
}
|
||||
|
||||
return nil, m
|
||||
}
|
||||
|
||||
//把结构体转换成yaml的字符串
|
||||
func GetStructToYaml(object interface{}) (error, string) {
|
||||
result, err := yaml.Marshal(object)
|
||||
if err != nil {
|
||||
return err, ""
|
||||
}
|
||||
return nil, string(result)
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* @Author : huangzj
|
||||
* @Time : 2020/12/16 11:56
|
||||
* @Description:
|
||||
*/
|
||||
|
||||
package yaml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type MySQLConfig struct {
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
DbName string `yaml:"dbname"`
|
||||
}
|
||||
type RedisConfig struct {
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
Auth string `yaml:"-"` //标签表示不显示
|
||||
}
|
||||
type NginxProxyConfig struct {
|
||||
Counter int `yaml:"counter"`
|
||||
NginxList []string `yaml:"nginx_list"`
|
||||
}
|
||||
|
||||
type ServiceYaml struct {
|
||||
MySQL MySQLConfig `yaml:"mysql"`
|
||||
Redis RedisConfig `yaml:"redis"`
|
||||
NginxProxy NginxProxyConfig `yaml:"nginx_proxy"`
|
||||
}
|
||||
|
||||
func TestYamlToStructByFile(t *testing.T) {
|
||||
config := new(ServiceYaml)
|
||||
_ = GetYamlToStructByFile("config.yaml", &config)
|
||||
|
||||
fmt.Printf("MySQL host : %s, port : %d, user : %s, password : %s, db_name : %s \n",
|
||||
config.MySQL.Host, config.MySQL.Port, config.MySQL.User, config.MySQL.Password, config.MySQL.DbName)
|
||||
|
||||
fmt.Printf("Redis host : %s, port %d, auth : %s\n",
|
||||
config.Redis.Host, config.Redis.Port, config.Redis.Auth)
|
||||
|
||||
fmt.Printf("Vip Counter: %d, Vip List : %v\n", config.NginxProxy.Counter, config.NginxProxy.NginxList)
|
||||
}
|
||||
|
||||
func TestYamlToStructByString(t *testing.T) {
|
||||
config := new(ServiceYaml)
|
||||
fileByte, _ := ioutil.ReadFile("config.yaml")
|
||||
_ = GetYamlToStructByString(string(fileByte), &config)
|
||||
|
||||
fmt.Printf("MySQL host : %s, port : %d, user : %s, password : %s, db_name : %s \n",
|
||||
config.MySQL.Host, config.MySQL.Port, config.MySQL.User, config.MySQL.Password, config.MySQL.DbName)
|
||||
|
||||
fmt.Printf("Redis host : %s, port %d, auth : %s\n",
|
||||
config.Redis.Host, config.Redis.Port, config.Redis.Auth)
|
||||
|
||||
fmt.Printf("Vip Counter: %d, Vip List : %v\n", config.NginxProxy.Counter, config.NginxProxy.NginxList)
|
||||
}
|
||||
|
||||
func TestYamlToMap(t *testing.T) {
|
||||
fileByte, _ := ioutil.ReadFile("config.yaml")
|
||||
_, m := GetYamlToMap(string(fileByte))
|
||||
for key, value := range m {
|
||||
fmt.Println(fmt.Sprintf("key:%v ,value: %v", key, value))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStructToYaml(t *testing.T) {
|
||||
config := new(ServiceYaml)
|
||||
fileByte, _ := ioutil.ReadFile("config.yaml")
|
||||
_ = GetYamlToStructByString(string(fileByte), &config)
|
||||
_, result := GetStructToYaml(config)
|
||||
fmt.Println(result)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
mysql:
|
||||
user: root
|
||||
password: 123456
|
||||
host: 192.198.1.1
|
||||
port: 3306
|
||||
dbname: mdb
|
||||
redis:
|
||||
host: 192.168.1.1
|
||||
port: 1234
|
||||
auth: 123456
|
||||
nginx_proxy:
|
||||
counter: 3
|
||||
nginx_list: [
|
||||
192.168.1.1,
|
||||
192.168.1.2,
|
||||
192.168.1.3
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
获取包命令: https://gopkg.in/yaml.v2
|
||||
|
||||
文档地址: https://gopkg.in/yaml.v2
|
||||
|
||||
参考地址:http://www.topgoer.com/%E5%85%B6%E4%BB%96/Yaml%E7%BC%96%E7%A0%81%E5%92%8C%E8%A7%A3%E7%A0%81.html
|
||||
-1
@@ -2,7 +2,6 @@ sudo: false
|
||||
|
||||
language: go
|
||||
go:
|
||||
- 1.5
|
||||
- 1.7
|
||||
|
||||
before_install:
|
||||
|
||||
+197
-10
@@ -1,14 +1,201 @@
|
||||
Copyright 2016 Ahmet Alp Balkan
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2016 Ahmet Alp Balkan
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
+91
-10
@@ -1,16 +1,21 @@
|
||||
# go-linq [](https://godoc.org/github.com/ahmetalpbalkan/go-linq) [](https://travis-ci.org/ahmetalpbalkan/go-linq) [](https://coveralls.io/github/ahmetalpbalkan/go-linq?branch=master) [](https://goreportcard.com/report/github.com/ahmetalpbalkan/go-linq)
|
||||
A powerful language integrated query (LINQ) library for Go.
|
||||
* Written in vanilla Go!
|
||||
* Safe for concurrent use
|
||||
* Written in vanilla Go, no dependencies!
|
||||
* Complete lazy evaluation with iterator pattern
|
||||
* Safe for concurrent use
|
||||
* Supports generic functions to make your code cleaner and free of type assertions
|
||||
* Supports arrays, slices, maps, strings, channels and custom collections
|
||||
(collection needs to implement `Iterable` interface and element - `Comparable`
|
||||
interface)
|
||||
|
||||
## Installation
|
||||
|
||||
$ go get github.com/ahmetalpbalkan/go-linq
|
||||
|
||||
We recommend using a dependency manager (e.g. [govendor][govendor] or
|
||||
[godep][godep]) to maintain a local copy of this package in your project.
|
||||
|
||||
[govendor]: https://github.com/kardianos/govendor
|
||||
[godep]: https://github.com/tools/godep/
|
||||
|
||||
> :warning: :warning: `go-linq` has recently introduced _breaking API changes_
|
||||
> with v2.0.0. See [release notes](#release-notes) for details. v2.0.0 comes with
|
||||
> a refined interface, dramatically increased performance and memory efficiency,
|
||||
@@ -26,16 +31,20 @@ Usage is as easy as chaining methods like:
|
||||
|
||||
`From(slice)` `.Where(predicate)` `.Select(selector)` `.Union(data)`
|
||||
|
||||
**Example: Find all owners of cars manufactured from 2015**
|
||||
**Example 1: Find all owners of cars manufactured after 2015**
|
||||
|
||||
```go
|
||||
import . "github.com/ahmetalpbalkan/go-linq"
|
||||
|
||||
type Car struct {
|
||||
id, year int
|
||||
year int
|
||||
owner, model string
|
||||
}
|
||||
|
||||
owners := []string{}
|
||||
...
|
||||
|
||||
|
||||
var owners []string
|
||||
|
||||
From(cars).Where(func(c interface{}) bool {
|
||||
return c.(Car).year >= 2015
|
||||
@@ -44,7 +53,21 @@ From(cars).Where(func(c interface{}) bool {
|
||||
}).ToSlice(&owners)
|
||||
```
|
||||
|
||||
**Example: Find the author who has written the most books**
|
||||
Or, you can use generic functions, like `WhereT` and `SelectT` to simplify your code
|
||||
(at a performance penalty):
|
||||
|
||||
```go
|
||||
var owners []string
|
||||
|
||||
From(cars).WhereT(func(c Car) bool {
|
||||
return c.year >= 2015
|
||||
}).SelectT(func(c Car) string {
|
||||
return c.owner
|
||||
}).ToSlice(&owners)
|
||||
```
|
||||
|
||||
**Example 2: Find the author who has written the most books**
|
||||
|
||||
```go
|
||||
import . "github.com/ahmetalpbalkan/go-linq"
|
||||
|
||||
@@ -71,7 +94,8 @@ author := From(books).SelectMany( // make a flat array of authors
|
||||
}).First() // take the first author
|
||||
```
|
||||
|
||||
**Example: Implement a custom method that leaves only values greater than the specified threshold**
|
||||
**Example 3: Implement a custom method that leaves only values greater than the specified threshold**
|
||||
|
||||
```go
|
||||
type MyQuery Query
|
||||
|
||||
@@ -96,10 +120,67 @@ func (q MyQuery) GreaterThan(threshold int) Query {
|
||||
result := MyQuery(Range(1,10)).GreaterThan(5).Results()
|
||||
```
|
||||
|
||||
**More examples** can be found in [documentation](https://godoc.org/github.com/ahmetalpbalkan/go-linq).
|
||||
## Generic Functions
|
||||
|
||||
Although Go doesn't implement generics, with some reflection tricks, you can use go-linq without
|
||||
typing `interface{}`s and type assertions. This will introduce a performance penalty (5x-10x slower)
|
||||
but will yield in a cleaner and more readable code.
|
||||
|
||||
Methods with `T` suffix (such as `WhereT`) accept functions with generic types. So instead of
|
||||
|
||||
.Select(func(v interface{}) interface{} {...})
|
||||
|
||||
you can type:
|
||||
|
||||
.SelectT(func(v YourType) YourOtherType {...})
|
||||
|
||||
This will make your code free of `interface{}` and type assertions.
|
||||
|
||||
**Example 4: "MapReduce" in a slice of string sentences to list the top 5 most used words using generic functions**
|
||||
|
||||
```go
|
||||
var results []string
|
||||
|
||||
From(sentences).
|
||||
// split sentences to words
|
||||
SelectManyT(func(sentence string) Query {
|
||||
return From(strings.Split(sentence, " "))
|
||||
}).
|
||||
// group the words
|
||||
GroupByT(
|
||||
func(word string) string { return word },
|
||||
func(word string) string { return word },
|
||||
).
|
||||
// order by count
|
||||
OrderByDescendingT(func(wordGroup Group) int {
|
||||
return len(wordGroup.Group)
|
||||
}).
|
||||
// order by the word
|
||||
ThenByT(func(wordGroup Group) string {
|
||||
return wordGroup.Key.(string)
|
||||
}).
|
||||
Take(5). // take the top 5
|
||||
// project the words using the index as rank
|
||||
SelectIndexedT(func(index int, wordGroup Group) string {
|
||||
return fmt.Sprintf("Rank: #%d, Word: %s, Counts: %d", index+1, wordGroup.Key, len(wordGroup.Group))
|
||||
}).
|
||||
ToSlice(&results)
|
||||
```
|
||||
|
||||
**More examples** can be found in the [documentation](https://godoc.org/github.com/ahmetalpbalkan/go-linq).
|
||||
|
||||
## Release Notes
|
||||
|
||||
~~~
|
||||
v3.0.0 (2017-01-10)
|
||||
* Breaking change: ToSlice() now overwrites existing slice starting
|
||||
from index 0 and grows/reslices it as needed.
|
||||
* Generic methods support (thanks @cleitonmarx!)
|
||||
- Accepting parametrized functions was originally proposed in #26
|
||||
- You can now avoid type assertions and interface{}s
|
||||
- Functions with generic methods are named as "MethodNameT" and
|
||||
signature for the existing LINQ methods are unchanged.
|
||||
* Added ForEach(), ForEachIndexed() and AggregateWithSeedBy().
|
||||
|
||||
v2.0.0 (2016-09-02)
|
||||
* IMPORTANT: This release is a BREAKING CHANGE. The old version
|
||||
|
||||
+124
-19
@@ -2,16 +2,15 @@ package linq
|
||||
|
||||
// Aggregate applies an accumulator function over a sequence.
|
||||
//
|
||||
// Aggregate method makes it simple to perform a calculation over a sequence of values.
|
||||
// This method works by calling f() one time for each element in source
|
||||
// except the first one. Each time f() is called, Aggregate passes both
|
||||
// the element from the sequence and an aggregated value (as the first argument to f()).
|
||||
// The first element of source is used as the initial aggregate value.
|
||||
// The result of f() replaces the previous aggregated value.
|
||||
// Aggregate method makes it simple to perform a calculation over a sequence of
|
||||
// values. This method works by calling f() one time for each element in source
|
||||
// except the first one. Each time f() is called, Aggregate passes both the
|
||||
// element from the sequence and an aggregated value (as the first argument to
|
||||
// f()). The first element of source is used as the initial aggregate value. The
|
||||
// result of f() replaces the previous aggregated value.
|
||||
//
|
||||
// Aggregate returns the final result of f().
|
||||
func (q Query) Aggregate(
|
||||
f func(interface{}, interface{}) interface{}) interface{} {
|
||||
func (q Query) Aggregate(f func(interface{}, interface{}) interface{}) interface{} {
|
||||
next := q.Iterate()
|
||||
|
||||
result, any := next()
|
||||
@@ -26,21 +25,40 @@ func (q Query) Aggregate(
|
||||
return result
|
||||
}
|
||||
|
||||
// AggregateWithSeed applies an accumulator function over a sequence.
|
||||
// The specified seed value is used as the initial accumulator value.
|
||||
// AggregateT is the typed version of Aggregate.
|
||||
//
|
||||
// Aggregate method makes it simple to perform a calculation over a sequence of values.
|
||||
// This method works by calling f() one time for each element in source
|
||||
// except the first one. Each time f() is called, Aggregate passes both
|
||||
// the element from the sequence and an aggregated value (as the first argument to f()).
|
||||
// The value of the seed parameter is used as the initial aggregate value.
|
||||
// - f is of type: func(TSource, TSource) TSource
|
||||
//
|
||||
// NOTE: Aggregate has better performance than AggregateT.
|
||||
func (q Query) AggregateT(f interface{}) interface{} {
|
||||
fGenericFunc, err := newGenericFunc(
|
||||
"AggregateT", "f", f,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fFunc := func(result interface{}, current interface{}) interface{} {
|
||||
return fGenericFunc.Call(result, current)
|
||||
}
|
||||
|
||||
return q.Aggregate(fFunc)
|
||||
}
|
||||
|
||||
// AggregateWithSeed applies an accumulator function over a sequence. The
|
||||
// specified seed value is used as the initial accumulator value.
|
||||
//
|
||||
// Aggregate method makes it simple to perform a calculation over a sequence of
|
||||
// values. This method works by calling f() one time for each element in source
|
||||
// except the first one. Each time f() is called, Aggregate passes both the
|
||||
// element from the sequence and an aggregated value (as the first argument to
|
||||
// f()). The value of the seed parameter is used as the initial aggregate value.
|
||||
// The result of f() replaces the previous aggregated value.
|
||||
//
|
||||
// Aggregate returns the final result of f().
|
||||
func (q Query) AggregateWithSeed(
|
||||
seed interface{},
|
||||
f func(interface{}, interface{}) interface{},
|
||||
) interface{} {
|
||||
func (q Query) AggregateWithSeed(seed interface{},
|
||||
f func(interface{}, interface{}) interface{}) interface{} {
|
||||
|
||||
next := q.Iterate()
|
||||
result := seed
|
||||
@@ -51,3 +69,90 @@ func (q Query) AggregateWithSeed(
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// AggregateWithSeedT is the typed version of AggregateWithSeed.
|
||||
//
|
||||
// - f is of type "func(TAccumulate, TSource) TAccumulate"
|
||||
//
|
||||
// NOTE: AggregateWithSeed has better performance than
|
||||
// AggregateWithSeedT.
|
||||
func (q Query) AggregateWithSeedT(seed interface{},
|
||||
f interface{}) interface{} {
|
||||
fGenericFunc, err := newGenericFunc(
|
||||
"AggregateWithSeed", "f", f,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fFunc := func(result interface{}, current interface{}) interface{} {
|
||||
return fGenericFunc.Call(result, current)
|
||||
}
|
||||
|
||||
return q.AggregateWithSeed(seed, fFunc)
|
||||
}
|
||||
|
||||
// AggregateWithSeedBy applies an accumulator function over a sequence. The
|
||||
// specified seed value is used as the initial accumulator value, and the
|
||||
// specified function is used to select the result value.
|
||||
//
|
||||
// Aggregate method makes it simple to perform a calculation over a sequence of
|
||||
// values. This method works by calling f() one time for each element in source.
|
||||
// Each time func is called, Aggregate passes both the element from the sequence
|
||||
// and an aggregated value (as the first argument to func). The value of the
|
||||
// seed parameter is used as the initial aggregate value. The result of func
|
||||
// replaces the previous aggregated value.
|
||||
//
|
||||
// The final result of func is passed to resultSelector to obtain the final
|
||||
// result of Aggregate.
|
||||
func (q Query) AggregateWithSeedBy(seed interface{},
|
||||
f func(interface{}, interface{}) interface{},
|
||||
resultSelector func(interface{}) interface{}) interface{} {
|
||||
|
||||
next := q.Iterate()
|
||||
result := seed
|
||||
|
||||
for current, ok := next(); ok; current, ok = next() {
|
||||
result = f(result, current)
|
||||
}
|
||||
|
||||
return resultSelector(result)
|
||||
}
|
||||
|
||||
// AggregateWithSeedByT is the typed version of AggregateWithSeedBy.
|
||||
//
|
||||
// - f is of type "func(TAccumulate, TSource) TAccumulate"
|
||||
// - resultSelectorFn is of type "func(TAccumulate) TResult"
|
||||
//
|
||||
// NOTE: AggregateWithSeedBy has better performance than
|
||||
// AggregateWithSeedByT.
|
||||
func (q Query) AggregateWithSeedByT(seed interface{},
|
||||
f interface{},
|
||||
resultSelectorFn interface{}) interface{} {
|
||||
fGenericFunc, err := newGenericFunc(
|
||||
"AggregateWithSeedByT", "f", f,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fFunc := func(result interface{}, current interface{}) interface{} {
|
||||
return fGenericFunc.Call(result, current)
|
||||
}
|
||||
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"AggregateWithSeedByT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(result interface{}) interface{} {
|
||||
return resultSelectorGenericFunc.Call(result)
|
||||
}
|
||||
|
||||
return q.AggregateWithSeedBy(seed, fFunc, resultSelectorFunc)
|
||||
}
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@ package linq
|
||||
|
||||
type comparer func(interface{}, interface{}) int
|
||||
|
||||
// Comparable is an interface that has to be implemented by a
|
||||
// custom collection elememts in order to work with linq.
|
||||
// Comparable is an interface that has to be implemented by a custom collection
|
||||
// elememts in order to work with linq.
|
||||
//
|
||||
// Example:
|
||||
// func (f foo) CompareTo(c Comparable) int {
|
||||
|
||||
+6
-6
@@ -1,7 +1,7 @@
|
||||
package linq
|
||||
|
||||
// Append inserts an item to the end of a collection,
|
||||
// so it becomes the last item.
|
||||
// Append inserts an item to the end of a collection, so it becomes the last
|
||||
// item.
|
||||
func (q Query) Append(item interface{}) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -28,8 +28,8 @@ func (q Query) Append(item interface{}) Query {
|
||||
// Concat concatenates two collections.
|
||||
//
|
||||
// The Concat method differs from the Union method because the Concat method
|
||||
// returns all the original elements in the input sequences.
|
||||
// The Union method returns only unique elements.
|
||||
// returns all the original elements in the input sequences. The Union method
|
||||
// returns only unique elements.
|
||||
func (q Query) Concat(q2 Query) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -53,8 +53,8 @@ func (q Query) Concat(q2 Query) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// Prepend inserts an item to the beginning of a collection,
|
||||
// so it becomes the first item.
|
||||
// Prepend inserts an item to the beginning of a collection, so it becomes the
|
||||
// first item.
|
||||
func (q Query) Prepend(item interface{}) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
|
||||
+28
-5
@@ -1,7 +1,7 @@
|
||||
package linq
|
||||
|
||||
// Distinct method returns distinct elements from a collection.
|
||||
// The result is an unordered collection that contains no duplicate values.
|
||||
// Distinct method returns distinct elements from a collection. The result is an
|
||||
// unordered collection that contains no duplicate values.
|
||||
func (q Query) Distinct() Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -22,11 +22,11 @@ func (q Query) Distinct() Query {
|
||||
}
|
||||
}
|
||||
|
||||
// Distinct method returns distinct elements from a collection.
|
||||
// The result is an ordered collection that contains no duplicate values.
|
||||
// Distinct method returns distinct elements from a collection. The result is an
|
||||
// ordered collection that contains no duplicate values.
|
||||
//
|
||||
// NOTE: Distinct method on OrderedQuery type has better performance than
|
||||
// Distinct method on Query type
|
||||
// Distinct method on Query type.
|
||||
func (oq OrderedQuery) Distinct() OrderedQuery {
|
||||
return OrderedQuery{
|
||||
orders: oq.orders,
|
||||
@@ -73,3 +73,26 @@ func (q Query) DistinctBy(selector func(interface{}) interface{}) Query {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DistinctByT is the typed version of DistinctBy.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource) TSource".
|
||||
//
|
||||
// NOTE: DistinctBy has better performance than DistinctByT.
|
||||
func (q Query) DistinctByT(selectorFn interface{}) Query {
|
||||
selectorFunc, ok := selectorFn.(func(interface{}) interface{})
|
||||
if !ok {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"DistinctByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc = func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
}
|
||||
return q.DistinctBy(selectorFunc)
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
// Package linq provides methods for querying and manipulating
|
||||
// slices, arrays, maps, strings, channels and collections.
|
||||
// Package linq provides methods for querying and manipulating slices, arrays,
|
||||
// maps, strings, channels and collections.
|
||||
//
|
||||
// Authors: Alexander Kalankhodzhaev (kalan), Ahmet Alp Balkan
|
||||
// Authors: Alexander Kalankhodzhaev (kalan), Ahmet Alp Balkan, Cleiton Marques
|
||||
// Souza.
|
||||
package linq
|
||||
|
||||
+29
-9
@@ -1,8 +1,7 @@
|
||||
package linq
|
||||
|
||||
// Except produces the set difference of two sequences.
|
||||
// The set difference is the members of the first sequence
|
||||
// that don't appear in the second sequence.
|
||||
// Except produces the set difference of two sequences. The set difference is
|
||||
// the members of the first sequence that don't appear in the second sequence.
|
||||
func (q Query) Except(q2 Query) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -27,12 +26,11 @@ func (q Query) Except(q2 Query) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// ExceptBy invokes a transform function on each element of a collection
|
||||
// and produces the set difference of two sequences.
|
||||
// The set difference is the members of the first sequence
|
||||
// that don't appear in the second sequence.
|
||||
func (q Query) ExceptBy(
|
||||
q2 Query, selector func(interface{}) interface{}) Query {
|
||||
// ExceptBy invokes a transform function on each element of a collection and
|
||||
// produces the set difference of two sequences. The set difference is the
|
||||
// members of the first sequence that don't appear in the second sequence.
|
||||
func (q Query) ExceptBy(q2 Query,
|
||||
selector func(interface{}) interface{}) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
next := q.Iterate()
|
||||
@@ -57,3 +55,25 @@ func (q Query) ExceptBy(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ExceptByT is the typed version of ExceptBy.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource) TSource"
|
||||
//
|
||||
// NOTE: ExceptBy has better performance than ExceptByT.
|
||||
func (q Query) ExceptByT(q2 Query,
|
||||
selectorFn interface{}) Query {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"ExceptByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return q.ExceptBy(q2, selectorFunc)
|
||||
}
|
||||
|
||||
+17
-17
@@ -5,30 +5,30 @@ import "reflect"
|
||||
// Iterator is an alias for function to iterate over data.
|
||||
type Iterator func() (item interface{}, ok bool)
|
||||
|
||||
// Query is the type returned from query functions.
|
||||
// It can be iterated manually as shown in the example.
|
||||
// Query is the type returned from query functions. It can be iterated manually
|
||||
// as shown in the example.
|
||||
type Query struct {
|
||||
Iterate func() Iterator
|
||||
}
|
||||
|
||||
// KeyValue is a type that is used to iterate over a map
|
||||
// (if query is created from a map). This type is also used by
|
||||
// ToMap() method to output result of a query into a map.
|
||||
// KeyValue is a type that is used to iterate over a map (if query is created
|
||||
// from a map). This type is also used by ToMap() method to output result of a
|
||||
// query into a map.
|
||||
type KeyValue struct {
|
||||
Key interface{}
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Iterable is an interface that has to be implemented by a
|
||||
// custom collection in order to work with linq.
|
||||
// Iterable is an interface that has to be implemented by a custom collection in
|
||||
// order to work with linq.
|
||||
type Iterable interface {
|
||||
Iterate() Iterator
|
||||
}
|
||||
|
||||
// From initializes a linq query with passed slice, array or map
|
||||
// as the source. String, channel or struct implementing Iterable
|
||||
// interface can be used as an input. In this case From delegates it
|
||||
// to FromString, FromChannel and FromIterable internally.
|
||||
// From initializes a linq query with passed slice, array or map as the source.
|
||||
// String, channel or struct implementing Iterable interface can be used as an
|
||||
// input. In this case From delegates it to FromString, FromChannel and
|
||||
// FromIterable internally.
|
||||
func From(source interface{}) Query {
|
||||
src := reflect.ValueOf(source)
|
||||
|
||||
@@ -84,8 +84,8 @@ func From(source interface{}) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// FromChannel initializes a linq query with passed channel,
|
||||
// linq iterates over channel until it is closed.
|
||||
// FromChannel initializes a linq query with passed channel, linq iterates over
|
||||
// channel until it is closed.
|
||||
func FromChannel(source <-chan interface{}) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -97,8 +97,8 @@ func FromChannel(source <-chan interface{}) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// FromString initializes a linq query with passed string,
|
||||
// linq iterates over runes of string.
|
||||
// FromString initializes a linq query with passed string, linq iterates over
|
||||
// runes of string.
|
||||
func FromString(source string) Query {
|
||||
runes := []rune(source)
|
||||
len := len(runes)
|
||||
@@ -120,8 +120,8 @@ func FromString(source string) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// FromIterable initializes a linq query with custom collection passed.
|
||||
// This collection has to implement Iterable interface, linq iterates over items,
|
||||
// FromIterable initializes a linq query with custom collection passed. This
|
||||
// collection has to implement Iterable interface, linq iterates over items,
|
||||
// that has to implement Comparable interface or be basic types.
|
||||
func FromIterable(source Iterable) Query {
|
||||
return Query{
|
||||
|
||||
+41
-8
@@ -6,14 +6,11 @@ type Group struct {
|
||||
Group []interface{}
|
||||
}
|
||||
|
||||
// GroupBy method groups the elements of a collection according
|
||||
// to a specified key selector function and projects the elements for each group
|
||||
// by using a specified function.
|
||||
func (q Query) GroupBy(
|
||||
keySelector func(interface{}) interface{},
|
||||
elementSelector func(interface{}) interface{},
|
||||
) Query {
|
||||
|
||||
// GroupBy method groups the elements of a collection according to a specified
|
||||
// key selector function and projects the elements for each group by using a
|
||||
// specified function.
|
||||
func (q Query) GroupBy(keySelector func(interface{}) interface{},
|
||||
elementSelector func(interface{}) interface{}) Query {
|
||||
return Query{
|
||||
func() Iterator {
|
||||
next := q.Iterate()
|
||||
@@ -46,3 +43,39 @@ func (q Query) GroupBy(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GroupByT is the typed version of GroupBy.
|
||||
//
|
||||
// - keySelectorFn is of type "func(TSource) TKey"
|
||||
// - elementSelectorFn is of type "func(TSource) TElement"
|
||||
//
|
||||
// NOTE: GroupBy has better performance than GroupByT.
|
||||
func (q Query) GroupByT(keySelectorFn interface{},
|
||||
elementSelectorFn interface{}) Query {
|
||||
keySelectorGenericFunc, err := newGenericFunc(
|
||||
"GroupByT", "keySelectorFn", keySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
keySelectorFunc := func(item interface{}) interface{} {
|
||||
return keySelectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
elementSelectorGenericFunc, err := newGenericFunc(
|
||||
"GroupByT", "elementSelectorFn", elementSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
elementSelectorFunc := func(item interface{}) interface{} {
|
||||
return elementSelectorGenericFunc.Call(item)
|
||||
|
||||
}
|
||||
|
||||
return q.GroupBy(keySelectorFunc, elementSelectorFunc)
|
||||
}
|
||||
|
||||
+69
-12
@@ -1,25 +1,27 @@
|
||||
package linq
|
||||
|
||||
import "reflect"
|
||||
|
||||
// GroupJoin correlates the elements of two collections based on key equality,
|
||||
// and groups the results.
|
||||
//
|
||||
// This method produces hierarchical results, which means that elements from outer query
|
||||
// are paired with collections of matching elements from inner. GroupJoin enables you
|
||||
// to base your results on a whole set of matches for each element of outer query.
|
||||
// This method produces hierarchical results, which means that elements from
|
||||
// outer query are paired with collections of matching elements from inner.
|
||||
// GroupJoin enables you to base your results on a whole set of matches for each
|
||||
// element of outer query.
|
||||
//
|
||||
// The resultSelector function is called only one time for each outer element
|
||||
// together with a collection of all the inner elements that match the outer element.
|
||||
// This differs from the Join method, in which the result selector function is invoked
|
||||
// on pairs that contain one element from outer and one element from inner.
|
||||
// together with a collection of all the inner elements that match the outer
|
||||
// element. This differs from the Join method, in which the result selector
|
||||
// function is invoked on pairs that contain one element from outer and one
|
||||
// element from inner.
|
||||
//
|
||||
// GroupJoin preserves the order of the elements of outer, and for each element of outer,
|
||||
// the order of the matching elements from inner.
|
||||
func (q Query) GroupJoin(
|
||||
inner Query,
|
||||
// GroupJoin preserves the order of the elements of outer, and for each element
|
||||
// of outer, the order of the matching elements from inner.
|
||||
func (q Query) GroupJoin(inner Query,
|
||||
outerKeySelector func(interface{}) interface{},
|
||||
innerKeySelector func(interface{}) interface{},
|
||||
resultSelector func(outer interface{}, inners []interface{}) interface{},
|
||||
) Query {
|
||||
resultSelector func(outer interface{}, inners []interface{}) interface{}) Query {
|
||||
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -48,3 +50,58 @@ func (q Query) GroupJoin(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GroupJoinT is the typed version of GroupJoin.
|
||||
//
|
||||
// - inner: The query to join to the outer query.
|
||||
// - outerKeySelectorFn is of type "func(TOuter) TKey"
|
||||
// - innerKeySelectorFn is of type "func(TInner) TKey"
|
||||
// - resultSelectorFn: is of type "func(TOuter, inners []TInner) TResult"
|
||||
//
|
||||
// NOTE: GroupJoin has better performance than GroupJoinT.
|
||||
func (q Query) GroupJoinT(inner Query,
|
||||
outerKeySelectorFn interface{},
|
||||
innerKeySelectorFn interface{},
|
||||
resultSelectorFn interface{}) Query {
|
||||
outerKeySelectorGenericFunc, err := newGenericFunc(
|
||||
"GroupJoinT", "outerKeySelectorFn", outerKeySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
outerKeySelectorFunc := func(item interface{}) interface{} {
|
||||
return outerKeySelectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
innerKeySelectorFuncGenericFunc, err := newGenericFunc(
|
||||
"GroupJoinT", "innerKeySelectorFn", innerKeySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
innerKeySelectorFunc := func(item interface{}) interface{} {
|
||||
return innerKeySelectorFuncGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"GroupJoinT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(outer interface{}, inners []interface{}) interface{} {
|
||||
innerSliceType := reflect.MakeSlice(resultSelectorGenericFunc.Cache.TypesIn[1], 0, 0)
|
||||
innersSlicePointer := reflect.New(innerSliceType.Type())
|
||||
From(inners).ToSlice(innersSlicePointer.Interface())
|
||||
innersTyped := reflect.Indirect(innersSlicePointer).Interface()
|
||||
return resultSelectorGenericFunc.Call(outer, innersTyped)
|
||||
}
|
||||
|
||||
return q.GroupJoin(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc)
|
||||
}
|
||||
|
||||
+28
-8
@@ -2,8 +2,8 @@ package linq
|
||||
|
||||
// Intersect produces the set intersection of the source collection and the
|
||||
// provided input collection. The intersection of two sets A and B is defined as
|
||||
// the set that contains all the elements of A that also appear in B,
|
||||
// but no other elements.
|
||||
// the set that contains all the elements of A that also appear in B, but no
|
||||
// other elements.
|
||||
func (q Query) Intersect(q2 Query) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -31,14 +31,12 @@ func (q Query) Intersect(q2 Query) Query {
|
||||
|
||||
// IntersectBy produces the set intersection of the source collection and the
|
||||
// provided input collection. The intersection of two sets A and B is defined as
|
||||
// the set that contains all the elements of A that also appear in B,
|
||||
// but no other elements.
|
||||
// the set that contains all the elements of A that also appear in B, but no
|
||||
// other elements.
|
||||
//
|
||||
// IntersectBy invokes a transform function on each element of both collections.
|
||||
func (q Query) IntersectBy(
|
||||
q2 Query,
|
||||
selector func(interface{}) interface{},
|
||||
) Query {
|
||||
func (q Query) IntersectBy(q2 Query,
|
||||
selector func(interface{}) interface{}) Query {
|
||||
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -65,3 +63,25 @@ func (q Query) IntersectBy(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// IntersectByT is the typed version of IntersectBy.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource) TSource"
|
||||
//
|
||||
// NOTE: IntersectBy has better performance than IntersectByT.
|
||||
func (q Query) IntersectByT(q2 Query,
|
||||
selectorFn interface{}) Query {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"IntersectByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return q.IntersectBy(q2, selectorFunc)
|
||||
}
|
||||
|
||||
+59
-10
@@ -2,20 +2,18 @@ package linq
|
||||
|
||||
// Join correlates the elements of two collection based on matching keys.
|
||||
//
|
||||
// A join refers to the operation of correlating the elements of two sources
|
||||
// of information based on a common key. Join brings the two information sources
|
||||
// and the keys by which they are matched together in one method call.
|
||||
// This differs from the use of SelectMany, which requires more than one method call
|
||||
// A join refers to the operation of correlating the elements of two sources of
|
||||
// information based on a common key. Join brings the two information sources
|
||||
// and the keys by which they are matched together in one method call. This
|
||||
// differs from the use of SelectMany, which requires more than one method call
|
||||
// to perform the same operation.
|
||||
//
|
||||
// Join preserves the order of the elements of outer collection,
|
||||
// and for each of these elements, the order of the matching elements of inner.
|
||||
func (q Query) Join(
|
||||
inner Query,
|
||||
// Join preserves the order of the elements of outer collection, and for each of
|
||||
// these elements, the order of the matching elements of inner.
|
||||
func (q Query) Join(inner Query,
|
||||
outerKeySelector func(interface{}) interface{},
|
||||
innerKeySelector func(interface{}) interface{},
|
||||
resultSelector func(outer interface{}, inner interface{}) interface{},
|
||||
) Query {
|
||||
resultSelector func(outer interface{}, inner interface{}) interface{}) Query {
|
||||
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -54,3 +52,54 @@ func (q Query) Join(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// JoinT is the typed version of Join.
|
||||
//
|
||||
// - outerKeySelectorFn is of type "func(TOuter) TKey"
|
||||
// - innerKeySelectorFn is of type "func(TInner) TKey"
|
||||
// - resultSelectorFn is of type "func(TOuter,TInner) TResult"
|
||||
//
|
||||
// NOTE: Join has better performance than JoinT.
|
||||
func (q Query) JoinT(inner Query,
|
||||
outerKeySelectorFn interface{},
|
||||
innerKeySelectorFn interface{},
|
||||
resultSelectorFn interface{}) Query {
|
||||
outerKeySelectorGenericFunc, err := newGenericFunc(
|
||||
"JoinT", "outerKeySelectorFn", outerKeySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
outerKeySelectorFunc := func(item interface{}) interface{} {
|
||||
return outerKeySelectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
innerKeySelectorFuncGenericFunc, err := newGenericFunc(
|
||||
"JoinT", "innerKeySelectorFn",
|
||||
innerKeySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
innerKeySelectorFunc := func(item interface{}) interface{} {
|
||||
return innerKeySelectorFuncGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"JoinT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(outer interface{}, inner interface{}) interface{} {
|
||||
return resultSelectorGenericFunc.Call(outer, inner)
|
||||
}
|
||||
|
||||
return q.Join(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc)
|
||||
}
|
||||
|
||||
+121
-20
@@ -8,18 +8,17 @@ type order struct {
|
||||
desc bool
|
||||
}
|
||||
|
||||
// OrderedQuery is the type returned from OrderBy, OrderByDescending
|
||||
// ThenBy and ThenByDescending functions.
|
||||
// OrderedQuery is the type returned from OrderBy, OrderByDescending ThenBy and
|
||||
// ThenByDescending functions.
|
||||
type OrderedQuery struct {
|
||||
Query
|
||||
original Query
|
||||
orders []order
|
||||
}
|
||||
|
||||
// OrderBy sorts the elements of a collection in ascending order.
|
||||
// Elements are sorted according to a key.
|
||||
func (q Query) OrderBy(
|
||||
selector func(interface{}) interface{}) OrderedQuery {
|
||||
// OrderBy sorts the elements of a collection in ascending order. Elements are
|
||||
// sorted according to a key.
|
||||
func (q Query) OrderBy(selector func(interface{}) interface{}) OrderedQuery {
|
||||
return OrderedQuery{
|
||||
orders: []order{{selector: selector}},
|
||||
original: q,
|
||||
@@ -43,10 +42,30 @@ func (q Query) OrderBy(
|
||||
}
|
||||
}
|
||||
|
||||
// OrderByT is the typed version of OrderBy.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource) TKey"
|
||||
//
|
||||
// NOTE: OrderBy has better performance than OrderByT.
|
||||
func (q Query) OrderByT(selectorFn interface{}) OrderedQuery {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"OrderByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return q.OrderBy(selectorFunc)
|
||||
}
|
||||
|
||||
// OrderByDescending sorts the elements of a collection in descending order.
|
||||
// Elements are sorted according to a key.
|
||||
func (q Query) OrderByDescending(
|
||||
selector func(interface{}) interface{}) OrderedQuery {
|
||||
func (q Query) OrderByDescending(selector func(interface{}) interface{}) OrderedQuery {
|
||||
return OrderedQuery{
|
||||
orders: []order{{selector: selector, desc: true}},
|
||||
original: q,
|
||||
@@ -70,9 +89,28 @@ func (q Query) OrderByDescending(
|
||||
}
|
||||
}
|
||||
|
||||
// ThenBy performs a subsequent ordering of the elements in a collection
|
||||
// in ascending order. This method enables you to specify multiple sort criteria
|
||||
// by applying any number of ThenBy or ThenByDescending methods.
|
||||
// OrderByDescendingT is the typed version of OrderByDescending.
|
||||
// - selectorFn is of type "func(TSource) TKey"
|
||||
// NOTE: OrderByDescending has better performance than OrderByDescendingT.
|
||||
func (q Query) OrderByDescendingT(selectorFn interface{}) OrderedQuery {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"OrderByDescendingT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return q.OrderByDescending(selectorFunc)
|
||||
}
|
||||
|
||||
// ThenBy performs a subsequent ordering of the elements in a collection in
|
||||
// ascending order. This method enables you to specify multiple sort criteria by
|
||||
// applying any number of ThenBy or ThenByDescending methods.
|
||||
func (oq OrderedQuery) ThenBy(
|
||||
selector func(interface{}) interface{}) OrderedQuery {
|
||||
return OrderedQuery{
|
||||
@@ -98,11 +136,29 @@ func (oq OrderedQuery) ThenBy(
|
||||
}
|
||||
}
|
||||
|
||||
// ThenByDescending performs a subsequent ordering of the elements in a collection
|
||||
// in descending order. This method enables you to specify multiple sort criteria
|
||||
// by applying any number of ThenBy or ThenByDescending methods.
|
||||
func (oq OrderedQuery) ThenByDescending(
|
||||
selector func(interface{}) interface{}) OrderedQuery {
|
||||
// ThenByT is the typed version of ThenBy.
|
||||
// - selectorFn is of type "func(TSource) TKey"
|
||||
// NOTE: ThenBy has better performance than ThenByT.
|
||||
func (oq OrderedQuery) ThenByT(selectorFn interface{}) OrderedQuery {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"ThenByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return oq.ThenBy(selectorFunc)
|
||||
}
|
||||
|
||||
// ThenByDescending performs a subsequent ordering of the elements in a
|
||||
// collection in descending order. This method enables you to specify multiple
|
||||
// sort criteria by applying any number of ThenBy or ThenByDescending methods.
|
||||
func (oq OrderedQuery) ThenByDescending(selector func(interface{}) interface{}) OrderedQuery {
|
||||
return OrderedQuery{
|
||||
orders: append(oq.orders, order{selector: selector, desc: true}),
|
||||
original: oq.original,
|
||||
@@ -126,10 +182,32 @@ func (oq OrderedQuery) ThenByDescending(
|
||||
}
|
||||
}
|
||||
|
||||
// Sort returns a new query by sorting elements with provided less function
|
||||
// in ascending order. The comparer function should return true if the parameter i
|
||||
// is less than j. While this method is uglier than chaining OrderBy, OrderByDescending,
|
||||
// ThenBy and ThenByDescending methods, it's performance is much better.
|
||||
// ThenByDescendingT is the typed version of ThenByDescending.
|
||||
// - selectorFn is of type "func(TSource) TKey"
|
||||
// NOTE: ThenByDescending has better performance than ThenByDescendingT.
|
||||
func (oq OrderedQuery) ThenByDescendingT(selectorFn interface{}) OrderedQuery {
|
||||
selectorFunc, ok := selectorFn.(func(interface{}) interface{})
|
||||
if !ok {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"ThenByDescending", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc = func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
}
|
||||
return oq.ThenByDescending(selectorFunc)
|
||||
}
|
||||
|
||||
// Sort returns a new query by sorting elements with provided less function in
|
||||
// ascending order. The comparer function should return true if the parameter i
|
||||
// is less than j. While this method is uglier than chaining OrderBy,
|
||||
// OrderByDescending, ThenBy and ThenByDescending methods, it's performance is
|
||||
// much better.
|
||||
func (q Query) Sort(less func(i, j interface{}) bool) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -150,6 +228,25 @@ func (q Query) Sort(less func(i, j interface{}) bool) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// SortT is the typed version of Sort.
|
||||
// - lessFn is of type "func(TSource,TSource) bool"
|
||||
// NOTE: Sort has better performance than SortT.
|
||||
func (q Query) SortT(lessFn interface{}) Query {
|
||||
lessGenericFunc, err := newGenericFunc(
|
||||
"SortT", "lessFn", lessFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
lessFunc := func(i, j interface{}) bool {
|
||||
return lessGenericFunc.Call(i, j).(bool)
|
||||
}
|
||||
|
||||
return q.Sort(lessFunc)
|
||||
}
|
||||
|
||||
type sorter struct {
|
||||
items []interface{}
|
||||
less func(i, j interface{}) bool
|
||||
@@ -173,6 +270,10 @@ func (q Query) sort(orders []order) (r []interface{}) {
|
||||
r = append(r, item)
|
||||
}
|
||||
|
||||
if len(r) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for i, j := range orders {
|
||||
orders[i].compare = getComparer(j.selector(r[0]))
|
||||
}
|
||||
|
||||
+300
-36
@@ -18,6 +18,27 @@ func (q Query) All(predicate func(interface{}) bool) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// AllT is the typed version of All.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: All has better performance than AllT.
|
||||
func (q Query) AllT(predicateFn interface{}) bool {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"AllT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.All(predicateFunc)
|
||||
}
|
||||
|
||||
// Any determines whether any element of a collection exists.
|
||||
func (q Query) Any() bool {
|
||||
_, ok := q.Iterate()()
|
||||
@@ -37,6 +58,28 @@ func (q Query) AnyWith(predicate func(interface{}) bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AnyWithT is the typed version of AnyWith.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: AnyWith has better performance than AnyWithT.
|
||||
func (q Query) AnyWithT(predicateFn interface{}) bool {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"AnyWithT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.AnyWith(predicateFunc)
|
||||
}
|
||||
|
||||
// Average computes the average of a collection of numeric values.
|
||||
func (q Query) Average() (r float64) {
|
||||
next := q.Iterate()
|
||||
@@ -104,8 +147,8 @@ func (q Query) Count() (r int) {
|
||||
return
|
||||
}
|
||||
|
||||
// CountWith returns a number that represents how many elements
|
||||
// in the specified collection satisfy a condition.
|
||||
// CountWith returns a number that represents how many elements in the specified
|
||||
// collection satisfy a condition.
|
||||
func (q Query) CountWith(predicate func(interface{}) bool) (r int) {
|
||||
next := q.Iterate()
|
||||
|
||||
@@ -118,14 +161,36 @@ func (q Query) CountWith(predicate func(interface{}) bool) (r int) {
|
||||
return
|
||||
}
|
||||
|
||||
// CountWithT is the typed version of CountWith.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: CountWith has better performance than CountWithT.
|
||||
func (q Query) CountWithT(predicateFn interface{}) int {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"CountWithT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.CountWith(predicateFunc)
|
||||
}
|
||||
|
||||
// First returns the first element of a collection.
|
||||
func (q Query) First() interface{} {
|
||||
item, _ := q.Iterate()()
|
||||
return item
|
||||
}
|
||||
|
||||
// FirstWith returns the first element of a collection that satisfies
|
||||
// a specified condition.
|
||||
// FirstWith returns the first element of a collection that satisfies a
|
||||
// specified condition.
|
||||
func (q Query) FirstWith(predicate func(interface{}) bool) interface{} {
|
||||
next := q.Iterate()
|
||||
|
||||
@@ -138,6 +203,99 @@ func (q Query) FirstWith(predicate func(interface{}) bool) interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FirstWithT is the typed version of FirstWith.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: FirstWith has better performance than FirstWithT.
|
||||
func (q Query) FirstWithT(predicateFn interface{}) interface{} {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"FirstWithT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.FirstWith(predicateFunc)
|
||||
}
|
||||
|
||||
// ForEach performs the specified action on each element of a collection.
|
||||
func (q Query) ForEach(action func(interface{})) {
|
||||
next := q.Iterate()
|
||||
|
||||
for item, ok := next(); ok; item, ok = next() {
|
||||
action(item)
|
||||
}
|
||||
}
|
||||
|
||||
// ForEachT is the typed version of ForEach.
|
||||
//
|
||||
// - actionFn is of type "func(TSource)"
|
||||
//
|
||||
// NOTE: ForEach has better performance than ForEachT.
|
||||
func (q Query) ForEachT(actionFn interface{}) {
|
||||
actionGenericFunc, err := newGenericFunc(
|
||||
"ForEachT", "actionFn", actionFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), nil),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
actionFunc := func(item interface{}) {
|
||||
actionGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
q.ForEach(actionFunc)
|
||||
}
|
||||
|
||||
// ForEachIndexed performs the specified action on each element of a collection.
|
||||
//
|
||||
// The first argument to action represents the zero-based index of that
|
||||
// element in the source collection. This can be useful if the elements are in a
|
||||
// known order and you want to do something with an element at a particular
|
||||
// index, for example. It can also be useful if you want to retrieve the index
|
||||
// of one or more elements. The second argument to action represents the
|
||||
// element to process.
|
||||
func (q Query) ForEachIndexed(action func(int, interface{})) {
|
||||
next := q.Iterate()
|
||||
index := 0
|
||||
|
||||
for item, ok := next(); ok; item, ok = next() {
|
||||
action(index, item)
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
// ForEachIndexedT is the typed version of ForEachIndexed.
|
||||
//
|
||||
// - actionFn is of type "func(int, TSource)"
|
||||
//
|
||||
// NOTE: ForEachIndexed has better performance than ForEachIndexedT.
|
||||
func (q Query) ForEachIndexedT(actionFn interface{}) {
|
||||
actionGenericFunc, err := newGenericFunc(
|
||||
"ForEachIndexedT", "actionFn", actionFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), nil),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
actionFunc := func(index int, item interface{}) {
|
||||
actionGenericFunc.Call(index, item)
|
||||
}
|
||||
|
||||
q.ForEachIndexed(actionFunc)
|
||||
}
|
||||
|
||||
// Last returns the last element of a collection.
|
||||
func (q Query) Last() (r interface{}) {
|
||||
next := q.Iterate()
|
||||
@@ -149,8 +307,8 @@ func (q Query) Last() (r interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// LastWith returns the last element of a collection that satisfies
|
||||
// a specified condition.
|
||||
// LastWith returns the last element of a collection that satisfies a specified
|
||||
// condition.
|
||||
func (q Query) LastWith(predicate func(interface{}) bool) (r interface{}) {
|
||||
next := q.Iterate()
|
||||
|
||||
@@ -163,6 +321,28 @@ func (q Query) LastWith(predicate func(interface{}) bool) (r interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// LastWithT is the typed version of LastWith.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: LastWith has better performance than LastWithT.
|
||||
func (q Query) LastWithT(predicateFn interface{}) interface{} {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"LastWithT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.LastWith(predicateFunc)
|
||||
}
|
||||
|
||||
// Max returns the maximum value in a collection of values.
|
||||
func (q Query) Max() (r interface{}) {
|
||||
next := q.Iterate()
|
||||
@@ -230,8 +410,8 @@ func (q Query) SequenceEqual(q2 Query) bool {
|
||||
return !ok2
|
||||
}
|
||||
|
||||
// Single returns the only element of a collection, and nil
|
||||
// if there is not exactly one element in the collection.
|
||||
// Single returns the only element of a collection, and nil if there is not
|
||||
// exactly one element in the collection.
|
||||
func (q Query) Single() interface{} {
|
||||
next := q.Iterate()
|
||||
item, ok := next()
|
||||
@@ -247,8 +427,8 @@ func (q Query) Single() interface{} {
|
||||
return item
|
||||
}
|
||||
|
||||
// SingleWith returns the only element of a collection that satisfies
|
||||
// a specified condition, and nil if more than one such element exists.
|
||||
// SingleWith returns the only element of a collection that satisfies a
|
||||
// specified condition, and nil if more than one such element exists.
|
||||
func (q Query) SingleWith(predicate func(interface{}) bool) (r interface{}) {
|
||||
next := q.Iterate()
|
||||
found := false
|
||||
@@ -267,10 +447,31 @@ func (q Query) SingleWith(predicate func(interface{}) bool) (r interface{}) {
|
||||
return
|
||||
}
|
||||
|
||||
// SingleWithT is the typed version of SingleWith.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: SingleWith has better performance than SingleWithT.
|
||||
func (q Query) SingleWithT(predicateFn interface{}) interface{} {
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"SingleWithT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.SingleWith(predicateFunc)
|
||||
}
|
||||
|
||||
// SumInts computes the sum of a collection of numeric values.
|
||||
//
|
||||
// Values can be of any integer type: int, int8, int16, int32, int64.
|
||||
// The result is int64. Method returns zero if collection contains no elements.
|
||||
// Values can be of any integer type: int, int8, int16, int32, int64. The result
|
||||
// is int64. Method returns zero if collection contains no elements.
|
||||
func (q Query) SumInts() (r int64) {
|
||||
next := q.Iterate()
|
||||
item, ok := next()
|
||||
@@ -290,8 +491,9 @@ func (q Query) SumInts() (r int64) {
|
||||
|
||||
// SumUInts computes the sum of a collection of numeric values.
|
||||
//
|
||||
// Values can be of any unsigned integer type: uint, uint8, uint16, uint32, uint64.
|
||||
// The result is uint64. Method returns zero if collection contains no elements.
|
||||
// Values can be of any unsigned integer type: uint, uint8, uint16, uint32,
|
||||
// uint64. The result is uint64. Method returns zero if collection contains no
|
||||
// elements.
|
||||
func (q Query) SumUInts() (r uint64) {
|
||||
next := q.Iterate()
|
||||
item, ok := next()
|
||||
@@ -330,8 +532,8 @@ func (q Query) SumFloats() (r float64) {
|
||||
return
|
||||
}
|
||||
|
||||
// ToChannel iterates over a collection and outputs each element
|
||||
// to a channel, then closes it.
|
||||
// ToChannel iterates over a collection and outputs each element to a channel,
|
||||
// then closes it.
|
||||
func (q Query) ToChannel(result chan<- interface{}) {
|
||||
next := q.Iterate()
|
||||
|
||||
@@ -343,8 +545,9 @@ func (q Query) ToChannel(result chan<- interface{}) {
|
||||
}
|
||||
|
||||
// ToMap iterates over a collection and populates result map with elements.
|
||||
// Collection elements have to be of KeyValue type to use this method.
|
||||
// To populate a map with elements of different type use ToMapBy method.
|
||||
// Collection elements have to be of KeyValue type to use this method. To
|
||||
// populate a map with elements of different type use ToMapBy method. ToMap
|
||||
// doesn't empty the result map before populating it.
|
||||
func (q Query) ToMap(result interface{}) {
|
||||
q.ToMapBy(
|
||||
result,
|
||||
@@ -356,15 +559,14 @@ func (q Query) ToMap(result interface{}) {
|
||||
})
|
||||
}
|
||||
|
||||
// ToMapBy iterates over a collection and populates result map with elements.
|
||||
// Functions keySelector and valueSelector are executed for each element of the collection
|
||||
// to generate key and value for the map. Generated key and value types must be assignable
|
||||
// to the map's key and value types.
|
||||
func (q Query) ToMapBy(
|
||||
result interface{},
|
||||
// ToMapBy iterates over a collection and populates the result map with
|
||||
// elements. Functions keySelector and valueSelector are executed for each
|
||||
// element of the collection to generate key and value for the map. Generated
|
||||
// key and value types must be assignable to the map's key and value types.
|
||||
// ToMapBy doesn't empty the result map before populating it.
|
||||
func (q Query) ToMapBy(result interface{},
|
||||
keySelector func(interface{}) interface{},
|
||||
valueSelector func(interface{}) interface{},
|
||||
) {
|
||||
valueSelector func(interface{}) interface{}) {
|
||||
res := reflect.ValueOf(result)
|
||||
m := reflect.Indirect(res)
|
||||
next := q.Iterate()
|
||||
@@ -379,16 +581,78 @@ func (q Query) ToMapBy(
|
||||
res.Elem().Set(m)
|
||||
}
|
||||
|
||||
// ToSlice iterates over a collection and populates result slice with elements.
|
||||
// Collection elements must be assignable to the slice's element type.
|
||||
func (q Query) ToSlice(result interface{}) {
|
||||
res := reflect.ValueOf(result)
|
||||
slice := reflect.Indirect(res)
|
||||
next := q.Iterate()
|
||||
|
||||
for item, ok := next(); ok; item, ok = next() {
|
||||
slice = reflect.Append(slice, reflect.ValueOf(item))
|
||||
// ToMapByT is the typed version of ToMapBy.
|
||||
//
|
||||
// - keySelectorFn is of type "func(TSource)TKey"
|
||||
// - valueSelectorFn is of type "func(TSource)TValue"
|
||||
//
|
||||
// NOTE: ToMapBy has better performance than ToMapByT.
|
||||
func (q Query) ToMapByT(result interface{},
|
||||
keySelectorFn interface{}, valueSelectorFn interface{}) {
|
||||
keySelectorGenericFunc, err := newGenericFunc(
|
||||
"ToMapByT", "keySelectorFn", keySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
res.Elem().Set(slice)
|
||||
keySelectorFunc := func(item interface{}) interface{} {
|
||||
return keySelectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
valueSelectorGenericFunc, err := newGenericFunc(
|
||||
"ToMapByT", "valueSelectorFn", valueSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
valueSelectorFunc := func(item interface{}) interface{} {
|
||||
return valueSelectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
q.ToMapBy(result, keySelectorFunc, valueSelectorFunc)
|
||||
}
|
||||
|
||||
// ToSlice iterates over a collection and saves the results in the slice pointed
|
||||
// by v. It overwrites the existing slice, starting from index 0.
|
||||
//
|
||||
// If the slice pointed by v has sufficient capacity, v will be pointed to a
|
||||
// resliced slice. If it does not, a new underlying array will be allocated and
|
||||
// v will point to it.
|
||||
func (q Query) ToSlice(v interface{}) {
|
||||
res := reflect.ValueOf(v)
|
||||
slice := reflect.Indirect(res)
|
||||
|
||||
cap := slice.Cap()
|
||||
res.Elem().Set(slice.Slice(0, cap)) // make len(slice)==cap(slice) from now on
|
||||
|
||||
next := q.Iterate()
|
||||
index := 0
|
||||
for item, ok := next(); ok; item, ok = next() {
|
||||
if index >= cap {
|
||||
slice, cap = grow(slice)
|
||||
}
|
||||
slice.Index(index).Set(reflect.ValueOf(item))
|
||||
index++
|
||||
}
|
||||
|
||||
// reslice the len(res)==cap(res) actual res size
|
||||
res.Elem().Set(slice.Slice(0, index))
|
||||
}
|
||||
|
||||
// grow grows the slice s by doubling its capacity, then it returns the new
|
||||
// slice (resliced to its full capacity) and the new capacity.
|
||||
func grow(s reflect.Value) (v reflect.Value, newCap int) {
|
||||
cap := s.Cap()
|
||||
if cap == 0 {
|
||||
cap = 1
|
||||
} else {
|
||||
cap *= 2
|
||||
}
|
||||
newSlice := reflect.MakeSlice(s.Type(), cap, cap)
|
||||
reflect.Copy(newSlice, s)
|
||||
return newSlice, cap
|
||||
}
|
||||
|
||||
+3
-3
@@ -2,9 +2,9 @@ package linq
|
||||
|
||||
// Reverse inverts the order of the elements in a collection.
|
||||
//
|
||||
// Unlike OrderBy, this sorting method does not consider the actual values themselves
|
||||
// in determining the order. Rather, it just returns the elements in the reverse order
|
||||
// from which they are produced by the underlying source.
|
||||
// Unlike OrderBy, this sorting method does not consider the actual values
|
||||
// themselves in determining the order. Rather, it just returns the elements in
|
||||
// the reverse order from which they are produced by the underlying source.
|
||||
func (q Query) Reverse() Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
|
||||
+65
-27
@@ -1,17 +1,16 @@
|
||||
package linq
|
||||
|
||||
// Select projects each element of a collection into a new form.
|
||||
// Returns a query with the result of invoking the transform function
|
||||
// on each element of original source.
|
||||
// Select projects each element of a collection into a new form. Returns a query
|
||||
// with the result of invoking the transform function on each element of
|
||||
// original source.
|
||||
//
|
||||
// This projection method requires the transform function, selector,
|
||||
// to produce one value for each value in the source collection.
|
||||
// If selector returns a value that is itself a collection,
|
||||
// it is up to the consumer to traverse the subcollections manually.
|
||||
// In such a situation, it might be better for your query to return a single
|
||||
// coalesced collection of values. To achieve this, use the SelectMany method
|
||||
// instead of Select. Although SelectMany works similarly to Select,
|
||||
// it differs in that the transform function returns a collection
|
||||
// This projection method requires the transform function, selector, to produce
|
||||
// one value for each value in the source collection. If selector returns a
|
||||
// value that is itself a collection, it is up to the consumer to traverse the
|
||||
// subcollections manually. In such a situation, it might be better for your
|
||||
// query to return a single coalesced collection of values. To achieve this, use
|
||||
// the SelectMany method instead of Select. Although SelectMany works similarly
|
||||
// to Select, it differs in that the transform function returns a collection
|
||||
// that is then expanded by SelectMany before it is returned.
|
||||
func (q Query) Select(selector func(interface{}) interface{}) Query {
|
||||
return Query{
|
||||
@@ -31,24 +30,44 @@ func (q Query) Select(selector func(interface{}) interface{}) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// SelectIndexed projects each element of a collection into a new form
|
||||
// by incorporating the element's index. Returns a query with the result
|
||||
// of invoking the transform function on each element of original source.
|
||||
// SelectT is the typed version of Select.
|
||||
// - selectorFn is of type "func(TSource)TResult"
|
||||
// NOTE: Select has better performance than SelectT.
|
||||
func (q Query) SelectT(selectorFn interface{}) Query {
|
||||
|
||||
selectGenericFunc, err := newGenericFunc(
|
||||
"SelectT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return q.Select(selectorFunc)
|
||||
}
|
||||
|
||||
// SelectIndexed projects each element of a collection into a new form by
|
||||
// incorporating the element's index. Returns a query with the result of
|
||||
// invoking the transform function on each element of original source.
|
||||
//
|
||||
// The first argument to selector represents the zero-based index of that element
|
||||
// in the source collection. This can be useful if the elements are in a known order
|
||||
// and you want to do something with an element at a particular index,
|
||||
// for example. It can also be useful if you want to retrieve the index of one
|
||||
// or more elements. The second argument to selector represents the element to process.
|
||||
// The first argument to selector represents the zero-based index of that
|
||||
// element in the source collection. This can be useful if the elements are in a
|
||||
// known order and you want to do something with an element at a particular
|
||||
// index, for example. It can also be useful if you want to retrieve the index
|
||||
// of one or more elements. The second argument to selector represents the
|
||||
// element to process.
|
||||
//
|
||||
// This projection method requires the transform function, selector,
|
||||
// to produce one value for each value in the source collection.
|
||||
// If selector returns a value that is itself a collection,
|
||||
// it is up to the consumer to traverse the subcollections manually.
|
||||
// In such a situation, it might be better for your query to return a single
|
||||
// coalesced collection of values. To achieve this, use the SelectMany method
|
||||
// instead of Select. Although SelectMany works similarly to Select,
|
||||
// it differs in that the transform function returns a collection
|
||||
// This projection method requires the transform function, selector, to produce
|
||||
// one value for each value in the source collection. If selector returns a
|
||||
// value that is itself a collection, it is up to the consumer to traverse the
|
||||
// subcollections manually. In such a situation, it might be better for your
|
||||
// query to return a single coalesced collection of values. To achieve this, use
|
||||
// the SelectMany method instead of Select. Although SelectMany works similarly
|
||||
// to Select, it differs in that the transform function returns a collection
|
||||
// that is then expanded by SelectMany before it is returned.
|
||||
func (q Query) SelectIndexed(selector func(int, interface{}) interface{}) Query {
|
||||
return Query{
|
||||
@@ -69,3 +88,22 @@ func (q Query) SelectIndexed(selector func(int, interface{}) interface{}) Query
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SelectIndexedT is the typed version of SelectIndexed.
|
||||
// - selectorFn is of type "func(int,TSource)TResult"
|
||||
// NOTE: SelectIndexed has better performance than SelectIndexedT.
|
||||
func (q Query) SelectIndexedT(selectorFn interface{}) Query {
|
||||
selectGenericFunc, err := newGenericFunc(
|
||||
"SelectIndexedT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(index int, item interface{}) interface{} {
|
||||
return selectGenericFunc.Call(index, item)
|
||||
}
|
||||
|
||||
return q.SelectIndexed(selectorFunc)
|
||||
}
|
||||
|
||||
+134
-20
@@ -32,14 +32,37 @@ func (q Query) SelectMany(selector func(interface{}) Query) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// SelectManyIndexed projects each element of a collection to a Query, iterates and
|
||||
// flattens the resulting collection into one collection.
|
||||
// SelectManyT is the typed version of SelectMany.
|
||||
//
|
||||
// The first argument to selector represents the zero-based index of that element
|
||||
// in the source collection. This can be useful if the elements are in a known order
|
||||
// and you want to do something with an element at a particular index, for example.
|
||||
// It can also be useful if you want to retrieve the index of one or more elements.
|
||||
// The second argument to selector represents the element to process.
|
||||
// - selectorFn is of type "func(TSource)Query"
|
||||
//
|
||||
// NOTE: SelectMany has better performance than SelectManyT.
|
||||
func (q Query) SelectManyT(selectorFn interface{}) Query {
|
||||
|
||||
selectManyGenericFunc, err := newGenericFunc(
|
||||
"SelectManyT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(Query))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(inner interface{}) Query {
|
||||
return selectManyGenericFunc.Call(inner).(Query)
|
||||
}
|
||||
return q.SelectMany(selectorFunc)
|
||||
|
||||
}
|
||||
|
||||
// SelectManyIndexed projects each element of a collection to a Query, iterates
|
||||
// and flattens the resulting collection into one collection.
|
||||
//
|
||||
// The first argument to selector represents the zero-based index of that
|
||||
// element in the source collection. This can be useful if the elements are in a
|
||||
// known order and you want to do something with an element at a particular
|
||||
// index, for example. It can also be useful if you want to retrieve the index
|
||||
// of one or more elements. The second argument to selector represents the
|
||||
// element to process.
|
||||
func (q Query) SelectManyIndexed(selector func(int, interface{}) Query) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -72,13 +95,33 @@ func (q Query) SelectManyIndexed(selector func(int, interface{}) Query) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// SelectManyIndexedT is the typed version of SelectManyIndexed.
|
||||
//
|
||||
// - selectorFn is of type "func(int,TSource)Query"
|
||||
//
|
||||
// NOTE: SelectManyIndexed has better performance than SelectManyIndexedT.
|
||||
func (q Query) SelectManyIndexedT(selectorFn interface{}) Query {
|
||||
|
||||
selectManyIndexedGenericFunc, err := newGenericFunc(
|
||||
"SelectManyIndexedT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(Query))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(index int, inner interface{}) Query {
|
||||
return selectManyIndexedGenericFunc.Call(index, inner).(Query)
|
||||
}
|
||||
|
||||
return q.SelectManyIndexed(selectorFunc)
|
||||
}
|
||||
|
||||
// SelectManyBy projects each element of a collection to a Query, iterates and
|
||||
// flattens the resulting collection into one collection, and invokes
|
||||
// a result selector function on each element therein.
|
||||
func (q Query) SelectManyBy(
|
||||
selector func(interface{}) Query,
|
||||
resultSelector func(interface{}, interface{}) interface{},
|
||||
) Query {
|
||||
// flattens the resulting collection into one collection, and invokes a result
|
||||
// selector function on each element therein.
|
||||
func (q Query) SelectManyBy(selector func(interface{}) Query,
|
||||
resultSelector func(interface{}, interface{}) interface{}) Query {
|
||||
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -103,18 +146,53 @@ func (q Query) SelectManyBy(
|
||||
}
|
||||
}
|
||||
|
||||
item = resultSelector(outer, item)
|
||||
item = resultSelector(item, outer)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SelectManyByIndexed projects each element of a collection to a Query, iterates and
|
||||
// flattens the resulting collection into one collection, and invokes
|
||||
// a result selector function on each element therein.
|
||||
// The index of each source element is used in the intermediate projected form
|
||||
// of that element.
|
||||
// SelectManyByT is the typed version of SelectManyBy.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource)Query"
|
||||
// - resultSelectorFn is of type "func(TSource,TCollection)TResult"
|
||||
//
|
||||
// NOTE: SelectManyBy has better performance than SelectManyByT.
|
||||
func (q Query) SelectManyByT(selectorFn interface{},
|
||||
resultSelectorFn interface{}) Query {
|
||||
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"SelectManyByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(Query))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(outer interface{}) Query {
|
||||
return selectorGenericFunc.Call(outer).(Query)
|
||||
}
|
||||
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"SelectManyByT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(outer interface{}, item interface{}) interface{} {
|
||||
return resultSelectorGenericFunc.Call(outer, item)
|
||||
}
|
||||
|
||||
return q.SelectManyBy(selectorFunc, resultSelectorFunc)
|
||||
}
|
||||
|
||||
// SelectManyByIndexed projects each element of a collection to a Query,
|
||||
// iterates and flattens the resulting collection into one collection, and
|
||||
// invokes a result selector function on each element therein. The index of each
|
||||
// source element is used in the intermediate projected form of that element.
|
||||
func (q Query) SelectManyByIndexed(selector func(int, interface{}) Query,
|
||||
resultSelector func(interface{}, interface{}) interface{}) Query {
|
||||
|
||||
@@ -143,9 +221,45 @@ func (q Query) SelectManyByIndexed(selector func(int, interface{}) Query,
|
||||
}
|
||||
}
|
||||
|
||||
item = resultSelector(outer, item)
|
||||
item = resultSelector(item, outer)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SelectManyByIndexedT is the typed version of SelectManyByIndexed.
|
||||
//
|
||||
// - selectorFn is of type "func(int,TSource)Query"
|
||||
// - resultSelectorFn is of type "func(TSource,TCollection)TResult"
|
||||
//
|
||||
// NOTE: SelectManyByIndexed has better performance than
|
||||
// SelectManyByIndexedT.
|
||||
func (q Query) SelectManyByIndexedT(selectorFn interface{},
|
||||
resultSelectorFn interface{}) Query {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"SelectManyByIndexedT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(Query))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(index int, outer interface{}) Query {
|
||||
return selectorGenericFunc.Call(index, outer).(Query)
|
||||
}
|
||||
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"SelectManyByIndexedT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(outer interface{}, item interface{}) interface{} {
|
||||
return resultSelectorGenericFunc.Call(outer, item)
|
||||
}
|
||||
|
||||
return q.SelectManyByIndexed(selectorFunc, resultSelectorFunc)
|
||||
}
|
||||
|
||||
+58
-15
@@ -1,7 +1,7 @@
|
||||
package linq
|
||||
|
||||
// Skip bypasses a specified number of elements in a collection
|
||||
// and then returns the remaining elements.
|
||||
// Skip bypasses a specified number of elements in a collection and then returns
|
||||
// the remaining elements.
|
||||
func (q Query) Skip(count int) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -22,13 +22,13 @@ func (q Query) Skip(count int) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// SkipWhile bypasses elements in a collection as long as a specified condition is true
|
||||
// and then returns the remaining elements.
|
||||
// SkipWhile bypasses elements in a collection as long as a specified condition
|
||||
// is true and then returns the remaining elements.
|
||||
//
|
||||
// This method tests each element by using predicate and skips the element
|
||||
// if the result is true. After the predicate function returns false for an element,
|
||||
// that element and the remaining elements in source are returned
|
||||
// and there are no more invocations of predicate.
|
||||
// This method tests each element by using predicate and skips the element if
|
||||
// the result is true. After the predicate function returns false for an
|
||||
// element, that element and the remaining elements in source are returned and
|
||||
// there are no more invocations of predicate.
|
||||
func (q Query) SkipWhile(predicate func(interface{}) bool) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -54,14 +54,36 @@ func (q Query) SkipWhile(predicate func(interface{}) bool) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// SkipWhileIndexed bypasses elements in a collection as long as a specified condition
|
||||
// is true and then returns the remaining elements. The element's index is used
|
||||
// in the logic of the predicate function.
|
||||
// SkipWhileT is the typed version of SkipWhile.
|
||||
//
|
||||
// This method tests each element by using predicate and skips the element
|
||||
// if the result is true. After the predicate function returns false for an element,
|
||||
// that element and the remaining elements in source are returned
|
||||
// and there are no more invocations of predicate.
|
||||
// - predicateFn is of type "func(TSource)bool"
|
||||
//
|
||||
// NOTE: SkipWhile has better performance than SkipWhileT.
|
||||
func (q Query) SkipWhileT(predicateFn interface{}) Query {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"SkipWhileT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.SkipWhile(predicateFunc)
|
||||
}
|
||||
|
||||
// SkipWhileIndexed bypasses elements in a collection as long as a specified
|
||||
// condition is true and then returns the remaining elements. The element's
|
||||
// index is used in the logic of the predicate function.
|
||||
//
|
||||
// This method tests each element by using predicate and skips the element if
|
||||
// the result is true. After the predicate function returns false for an
|
||||
// element, that element and the remaining elements in source are returned and
|
||||
// there are no more invocations of predicate.
|
||||
func (q Query) SkipWhileIndexed(predicate func(int, interface{}) bool) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -89,3 +111,24 @@ func (q Query) SkipWhileIndexed(predicate func(int, interface{}) bool) Query {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SkipWhileIndexedT is the typed version of SkipWhileIndexed.
|
||||
//
|
||||
// - predicateFn is of type "func(int,TSource)bool"
|
||||
//
|
||||
// NOTE: SkipWhileIndexed has better performance than SkipWhileIndexedT.
|
||||
func (q Query) SkipWhileIndexedT(predicateFn interface{}) Query {
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"SkipWhileIndexedT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(index int, item interface{}) bool {
|
||||
return predicateGenericFunc.Call(index, item).(bool)
|
||||
}
|
||||
|
||||
return q.SkipWhileIndexed(predicateFunc)
|
||||
}
|
||||
|
||||
+52
-7
@@ -1,6 +1,7 @@
|
||||
package linq
|
||||
|
||||
// Take returns a specified number of contiguous elements from the start of a collection.
|
||||
// Take returns a specified number of contiguous elements from the start of a
|
||||
// collection.
|
||||
func (q Query) Take(count int) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -19,8 +20,8 @@ func (q Query) Take(count int) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// TakeWhile returns elements from a collection as long as a specified condition is true,
|
||||
// and then skips the remaining elements.
|
||||
// TakeWhile returns elements from a collection as long as a specified condition
|
||||
// is true, and then skips the remaining elements.
|
||||
func (q Query) TakeWhile(predicate func(interface{}) bool) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -49,10 +50,33 @@ func (q Query) TakeWhile(predicate func(interface{}) bool) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// TakeWhileIndexed returns elements from a collection as long as a specified condition
|
||||
// is true. The element's index is used in the logic of the predicate function.
|
||||
// The first argument of predicate represents the zero-based index of the element
|
||||
// within collection. The second argument represents the element to test.
|
||||
// TakeWhileT is the typed version of TakeWhile.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource)bool"
|
||||
//
|
||||
// NOTE: TakeWhile has better performance than TakeWhileT.
|
||||
func (q Query) TakeWhileT(predicateFn interface{}) Query {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"TakeWhileT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.TakeWhile(predicateFunc)
|
||||
}
|
||||
|
||||
// TakeWhileIndexed returns elements from a collection as long as a specified
|
||||
// condition is true. The element's index is used in the logic of the predicate
|
||||
// function. The first argument of predicate represents the zero-based index of
|
||||
// the element within collection. The second argument represents the element to
|
||||
// test.
|
||||
func (q Query) TakeWhileIndexed(predicate func(int, interface{}) bool) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -82,3 +106,24 @@ func (q Query) TakeWhileIndexed(predicate func(int, interface{}) bool) Query {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TakeWhileIndexedT is the typed version of TakeWhileIndexed.
|
||||
//
|
||||
// - predicateFn is of type "func(int,TSource)bool"
|
||||
//
|
||||
// NOTE: TakeWhileIndexed has better performance than TakeWhileIndexedT.
|
||||
func (q Query) TakeWhileIndexedT(predicateFn interface{}) Query {
|
||||
whereFunc, err := newGenericFunc(
|
||||
"TakeWhileIndexedT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(index int, item interface{}) bool {
|
||||
return whereFunc.Call(index, item).(bool)
|
||||
}
|
||||
|
||||
return q.TakeWhileIndexed(predicateFunc)
|
||||
}
|
||||
|
||||
+3
-4
@@ -2,10 +2,9 @@ package linq
|
||||
|
||||
// Union produces the set union of two collections.
|
||||
//
|
||||
// This method excludes duplicates from the return set.
|
||||
// This is different behavior to the Concat method,
|
||||
// which returns all the elements in the input collection
|
||||
// including duplicates.
|
||||
// This method excludes duplicates from the return set. This is different
|
||||
// behavior to the Concat method, which returns all the elements in the input
|
||||
// collection including duplicates.
|
||||
func (q Query) Union(q2 Query) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
|
||||
+47
-4
@@ -19,11 +19,33 @@ func (q Query) Where(predicate func(interface{}) bool) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// WhereIndexed filters a collection of values based on a predicate.
|
||||
// Each element's index is used in the logic of the predicate function.
|
||||
// WhereT is the typed version of Where.
|
||||
//
|
||||
// The first argument represents the zero-based index of the element within collection.
|
||||
// The second argument of predicate represents the element to test.
|
||||
// - predicateFn is of type "func(TSource)bool"
|
||||
//
|
||||
// NOTE: Where has better performance than WhereT.
|
||||
func (q Query) WhereT(predicateFn interface{}) Query {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"WhereT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.Where(predicateFunc)
|
||||
}
|
||||
|
||||
// WhereIndexed filters a collection of values based on a predicate. Each
|
||||
// element's index is used in the logic of the predicate function.
|
||||
//
|
||||
// The first argument represents the zero-based index of the element within
|
||||
// collection. The second argument of predicate represents the element to test.
|
||||
func (q Query) WhereIndexed(predicate func(int, interface{}) bool) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -44,3 +66,24 @@ func (q Query) WhereIndexed(predicate func(int, interface{}) bool) Query {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WhereIndexedT is the typed version of WhereIndexed.
|
||||
//
|
||||
// - predicateFn is of type "func(int,TSource)bool"
|
||||
//
|
||||
// NOTE: WhereIndexed has better performance than WhereIndexedT.
|
||||
func (q Query) WhereIndexedT(predicateFn interface{}) Query {
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"WhereIndexedT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(index int, item interface{}) bool {
|
||||
return predicateGenericFunc.Call(index, item).(bool)
|
||||
}
|
||||
|
||||
return q.WhereIndexed(predicateFunc)
|
||||
}
|
||||
|
||||
+32
-12
@@ -1,19 +1,17 @@
|
||||
package linq
|
||||
|
||||
// Zip applies a specified function to the corresponding elements
|
||||
// of two collections, producing a collection of the results.
|
||||
// Zip applies a specified function to the corresponding elements of two
|
||||
// collections, producing a collection of the results.
|
||||
//
|
||||
// The method steps through the two input collections, applying function
|
||||
// resultSelector to corresponding elements of the two collections.
|
||||
// The method returns a collection of the values that are returned by resultSelector.
|
||||
// If the input collections do not have the same number of elements,
|
||||
// the method combines elements until it reaches the end of one of the collections.
|
||||
// For example, if one collection has three elements and the other one has four,
|
||||
// the result collection has only three elements.
|
||||
func (q Query) Zip(
|
||||
q2 Query,
|
||||
resultSelector func(interface{}, interface{}) interface{},
|
||||
) Query {
|
||||
// resultSelector to corresponding elements of the two collections. The method
|
||||
// returns a collection of the values that are returned by resultSelector. If
|
||||
// the input collections do not have the same number of elements, the method
|
||||
// combines elements until it reaches the end of one of the collections. For
|
||||
// example, if one collection has three elements and the other one has four, the
|
||||
// result collection has only three elements.
|
||||
func (q Query) Zip(q2 Query,
|
||||
resultSelector func(interface{}, interface{}) interface{}) Query {
|
||||
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -33,3 +31,25 @@ func (q Query) Zip(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ZipT is the typed version of Zip.
|
||||
//
|
||||
// - resultSelectorFn is of type "func(TFirst,TSecond)TResult"
|
||||
//
|
||||
// NOTE: Zip has better performance than ZipT.
|
||||
func (q Query) ZipT(q2 Query,
|
||||
resultSelectorFn interface{}) Query {
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"ZipT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(item1 interface{}, item2 interface{}) interface{} {
|
||||
return resultSelectorGenericFunc.Call(item1, item2)
|
||||
}
|
||||
|
||||
return q.Zip(q2, resultSelectorFunc)
|
||||
}
|
||||
|
||||
Vendored
+3
-1
@@ -1,4 +1,4 @@
|
||||
# github.com/ahmetb/go-linq v2.0.0-rc0+incompatible
|
||||
# github.com/ahmetb/go-linq v3.0.0+incompatible
|
||||
github.com/ahmetb/go-linq
|
||||
# github.com/go-ini/ini v1.57.0
|
||||
github.com/go-ini/ini
|
||||
@@ -8,3 +8,5 @@ github.com/json-iterator/go
|
||||
github.com/modern-go/concurrent
|
||||
# github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742
|
||||
github.com/modern-go/reflect2
|
||||
# gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v2
|
||||
|
||||
Reference in New Issue
Block a user