feat(Go-Tool):

2020/12/16: 添加YAML和结构体转换工具
This commit is contained in:
Huangzj
2020-12-16 14:23:38 +08:00
parent 61966551cd
commit dc80b557e3
32 changed files with 1700 additions and 324 deletions
+3 -11
View File
@@ -3,19 +3,11 @@ module Go-Tool
go 1.13 go 1.13
require ( require (
github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 // indirect github.com/ahmetb/go-linq v3.0.0+incompatible
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/go-ini/ini v1.57.0 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/json-iterator/go v1.1.10
github.com/kardianos/govendor v1.0.9 // indirect github.com/smartystreets/goconvey v1.6.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/stretchr/testify v1.5.1 // 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/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect gopkg.in/yaml.v2 v2.4.0
) )
+14 -34
View File
@@ -1,65 +1,45 @@
github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 h1:xGHheKK44eC6K0u5X+DZW/fRaR1LnDdqPHMZMWx5fv8= github.com/ahmetb/go-linq v3.0.0+incompatible h1:qQkjjOXKrKOTy83X8OpRmnKflXKQIL/mC/gMVVDMhOA=
github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= github.com/ahmetb/go-linq v3.0.0+incompatible/go.mod h1:PFffvbdbtw+QTB0WKRP0cNht7vnCfnGlEpak/DVg5cY=
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/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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.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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:Qwzj3wZQW+Plax5Ntj+GYe07DfGj1OH+aL1nMTMaNow=
github.com/go-ini/ini v1.57.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 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 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/kardianos/govendor v1.0.9/go.mod h1:yvmR6q9ZZ7nSF5Wvh40v0wfP+3TwwL8zYQp+itoZSVM= 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 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 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 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= 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/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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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/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-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
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=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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/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 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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=
+2
View File
@@ -70,6 +70,8 @@
2020/12/15:添加前缀树工具,实现前缀搜索和字符串搜索功能 2020/12/15:添加前缀树工具,实现前缀搜索和字符串搜索功能
2020/12/16: 添加YAML和结构体转换工具
# mod vendor模式加载包 # mod vendor模式加载包
通过go mod的方式加载的github上面的包会有报红的问题,但是包本身是可以运行的,这样就是会有一个问题,如果你想要点击去看方法的内容,没办法做到 通过go mod的方式加载的github上面的包会有报红的问题,但是包本身是可以运行的,这样就是会有一个问题,如果你想要点击去看方法的内容,没办法做到
+57
View File
@@ -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)
}
+79
View File
@@ -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)
}
+17
View File
@@ -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
]
+5
View File
@@ -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
View File
@@ -2,7 +2,6 @@ sudo: false
language: go language: go
go: go:
- 1.5
- 1.7 - 1.7
before_install: before_install:
+188 -1
View File
@@ -1,3 +1,191 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
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 Copyright 2016 Ahmet Alp Balkan
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,4 +199,3 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
+91 -10
View File
@@ -1,16 +1,21 @@
# go-linq [![GoDoc](https://godoc.org/github.com/ahmetalpbalkan/go-linq?status.svg)](https://godoc.org/github.com/ahmetalpbalkan/go-linq) [![Build Status](https://travis-ci.org/ahmetalpbalkan/go-linq.svg?branch=master)](https://travis-ci.org/ahmetalpbalkan/go-linq) [![Coverage Status](https://coveralls.io/repos/github/ahmetalpbalkan/go-linq/badge.svg?branch=master)](https://coveralls.io/github/ahmetalpbalkan/go-linq?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ahmetalpbalkan/go-linq)](https://goreportcard.com/report/github.com/ahmetalpbalkan/go-linq) # go-linq [![GoDoc](https://godoc.org/github.com/ahmetalpbalkan/go-linq?status.svg)](https://godoc.org/github.com/ahmetalpbalkan/go-linq) [![Build Status](https://travis-ci.org/ahmetalpbalkan/go-linq.svg?branch=master)](https://travis-ci.org/ahmetalpbalkan/go-linq) [![Coverage Status](https://coveralls.io/repos/github/ahmetalpbalkan/go-linq/badge.svg?branch=master)](https://coveralls.io/github/ahmetalpbalkan/go-linq?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ahmetalpbalkan/go-linq)](https://goreportcard.com/report/github.com/ahmetalpbalkan/go-linq)
A powerful language integrated query (LINQ) library for Go. A powerful language integrated query (LINQ) library for Go.
* Written in vanilla Go! * Written in vanilla Go, no dependencies!
* Safe for concurrent use
* Complete lazy evaluation with iterator pattern * 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 * Supports arrays, slices, maps, strings, channels and custom collections
(collection needs to implement `Iterable` interface and element - `Comparable`
interface)
## Installation ## Installation
$ go get github.com/ahmetalpbalkan/go-linq $ 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_ > :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 > 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, > 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)` `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 ```go
import . "github.com/ahmetalpbalkan/go-linq" import . "github.com/ahmetalpbalkan/go-linq"
type Car struct { type Car struct {
id, year int year int
owner, model string owner, model string
} }
owners := []string{} ...
var owners []string
From(cars).Where(func(c interface{}) bool { From(cars).Where(func(c interface{}) bool {
return c.(Car).year >= 2015 return c.(Car).year >= 2015
@@ -44,7 +53,21 @@ From(cars).Where(func(c interface{}) bool {
}).ToSlice(&owners) }).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 ```go
import . "github.com/ahmetalpbalkan/go-linq" 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 }).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 ```go
type MyQuery Query type MyQuery Query
@@ -96,10 +120,67 @@ func (q MyQuery) GreaterThan(threshold int) Query {
result := MyQuery(Range(1,10)).GreaterThan(5).Results() 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 ## 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) v2.0.0 (2016-09-02)
* IMPORTANT: This release is a BREAKING CHANGE. The old version * IMPORTANT: This release is a BREAKING CHANGE. The old version
+124 -19
View File
@@ -2,16 +2,15 @@ package linq
// Aggregate applies an accumulator function over a sequence. // Aggregate applies an accumulator function over a sequence.
// //
// Aggregate method makes it simple to perform a calculation over a sequence of values. // Aggregate method makes it simple to perform a calculation over a sequence of
// This method works by calling f() one time for each element in source // 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 // except the first one. Each time f() is called, Aggregate passes both the
// the element from the sequence and an aggregated value (as the first argument to f()). // element from the sequence and an aggregated value (as the first argument to
// The first element of source is used as the initial aggregate value. // f()). The first element of source is used as the initial aggregate value. The
// The result of f() replaces the previous aggregated value. // result of f() replaces the previous aggregated value.
// //
// Aggregate returns the final result of f(). // Aggregate returns the final result of f().
func (q Query) Aggregate( func (q Query) Aggregate(f func(interface{}, interface{}) interface{}) interface{} {
f func(interface{}, interface{}) interface{}) interface{} {
next := q.Iterate() next := q.Iterate()
result, any := next() result, any := next()
@@ -26,21 +25,40 @@ func (q Query) Aggregate(
return result return result
} }
// AggregateWithSeed applies an accumulator function over a sequence. // AggregateT is the typed version of Aggregate.
// 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. // - f is of type: func(TSource, TSource) TSource
// 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 // NOTE: Aggregate has better performance than AggregateT.
// the element from the sequence and an aggregated value (as the first argument to f()). func (q Query) AggregateT(f interface{}) interface{} {
// The value of the seed parameter is used as the initial aggregate value. 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. // The result of f() replaces the previous aggregated value.
// //
// Aggregate returns the final result of f(). // Aggregate returns the final result of f().
func (q Query) AggregateWithSeed( func (q Query) AggregateWithSeed(seed interface{},
seed interface{}, f func(interface{}, interface{}) interface{}) interface{} {
f func(interface{}, interface{}) interface{},
) interface{} {
next := q.Iterate() next := q.Iterate()
result := seed result := seed
@@ -51,3 +69,90 @@ func (q Query) AggregateWithSeed(
return result 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
View File
@@ -2,8 +2,8 @@ package linq
type comparer func(interface{}, interface{}) int type comparer func(interface{}, interface{}) int
// Comparable is an interface that has to be implemented by a // Comparable is an interface that has to be implemented by a custom collection
// custom collection elememts in order to work with linq. // elememts in order to work with linq.
// //
// Example: // Example:
// func (f foo) CompareTo(c Comparable) int { // func (f foo) CompareTo(c Comparable) int {
+6 -6
View File
@@ -1,7 +1,7 @@
package linq package linq
// Append inserts an item to the end of a collection, // Append inserts an item to the end of a collection, so it becomes the last
// so it becomes the last item. // item.
func (q Query) Append(item interface{}) Query { func (q Query) Append(item interface{}) Query {
return Query{ return Query{
Iterate: func() Iterator { Iterate: func() Iterator {
@@ -28,8 +28,8 @@ func (q Query) Append(item interface{}) Query {
// Concat concatenates two collections. // Concat concatenates two collections.
// //
// The Concat method differs from the Union method because the Concat method // The Concat method differs from the Union method because the Concat method
// returns all the original elements in the input sequences. // returns all the original elements in the input sequences. The Union method
// The Union method returns only unique elements. // returns only unique elements.
func (q Query) Concat(q2 Query) Query { func (q Query) Concat(q2 Query) Query {
return Query{ return Query{
Iterate: func() Iterator { Iterate: func() Iterator {
@@ -53,8 +53,8 @@ func (q Query) Concat(q2 Query) Query {
} }
} }
// Prepend inserts an item to the beginning of a collection, // Prepend inserts an item to the beginning of a collection, so it becomes the
// so it becomes the first item. // first item.
func (q Query) Prepend(item interface{}) Query { func (q Query) Prepend(item interface{}) Query {
return Query{ return Query{
Iterate: func() Iterator { Iterate: func() Iterator {
+28 -5
View File
@@ -1,7 +1,7 @@
package linq package linq
// Distinct method returns distinct elements from a collection. // Distinct method returns distinct elements from a collection. The result is an
// The result is an unordered collection that contains no duplicate values. // unordered collection that contains no duplicate values.
func (q Query) Distinct() Query { func (q Query) Distinct() Query {
return Query{ return Query{
Iterate: func() Iterator { Iterate: func() Iterator {
@@ -22,11 +22,11 @@ func (q Query) Distinct() Query {
} }
} }
// Distinct method returns distinct elements from a collection. // Distinct method returns distinct elements from a collection. The result is an
// The result is an ordered collection that contains no duplicate values. // ordered collection that contains no duplicate values.
// //
// NOTE: Distinct method on OrderedQuery type has better performance than // 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 { func (oq OrderedQuery) Distinct() OrderedQuery {
return OrderedQuery{ return OrderedQuery{
orders: oq.orders, 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
View File
@@ -1,5 +1,6 @@
// Package linq provides methods for querying and manipulating // Package linq provides methods for querying and manipulating slices, arrays,
// slices, arrays, maps, strings, channels and collections. // maps, strings, channels and collections.
// //
// Authors: Alexander Kalankhodzhaev (kalan), Ahmet Alp Balkan // Authors: Alexander Kalankhodzhaev (kalan), Ahmet Alp Balkan, Cleiton Marques
// Souza.
package linq package linq
+29 -9
View File
@@ -1,8 +1,7 @@
package linq package linq
// Except produces the set difference of two sequences. // Except produces the set difference of two sequences. The set difference is
// The set difference is the members of the first sequence // the members of the first sequence that don't appear in the second sequence.
// that don't appear in the second sequence.
func (q Query) Except(q2 Query) Query { func (q Query) Except(q2 Query) Query {
return Query{ return Query{
Iterate: func() Iterator { 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 // ExceptBy invokes a transform function on each element of a collection and
// and produces the set difference of two sequences. // produces the set difference of two sequences. The set difference is the
// The set difference is the members of the first sequence // members of the first sequence that don't appear in the second sequence.
// that don't appear in the second sequence. func (q Query) ExceptBy(q2 Query,
func (q Query) ExceptBy( selector func(interface{}) interface{}) Query {
q2 Query, selector func(interface{}) interface{}) Query {
return Query{ return Query{
Iterate: func() Iterator { Iterate: func() Iterator {
next := q.Iterate() 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
View File
@@ -5,30 +5,30 @@ import "reflect"
// Iterator is an alias for function to iterate over data. // Iterator is an alias for function to iterate over data.
type Iterator func() (item interface{}, ok bool) type Iterator func() (item interface{}, ok bool)
// Query is the type returned from query functions. // Query is the type returned from query functions. It can be iterated manually
// It can be iterated manually as shown in the example. // as shown in the example.
type Query struct { type Query struct {
Iterate func() Iterator Iterate func() Iterator
} }
// KeyValue is a type that is used to iterate over a map // KeyValue is a type that is used to iterate over a map (if query is created
// (if query is created from a map). This type is also used by // from a map). This type is also used by ToMap() method to output result of a
// ToMap() method to output result of a query into a map. // query into a map.
type KeyValue struct { type KeyValue struct {
Key interface{} Key interface{}
Value interface{} Value interface{}
} }
// Iterable is an interface that has to be implemented by a // Iterable is an interface that has to be implemented by a custom collection in
// custom collection in order to work with linq. // order to work with linq.
type Iterable interface { type Iterable interface {
Iterate() Iterator Iterate() Iterator
} }
// From initializes a linq query with passed slice, array or map // From initializes a linq query with passed slice, array or map as the source.
// as the source. String, channel or struct implementing Iterable // String, channel or struct implementing Iterable interface can be used as an
// interface can be used as an input. In this case From delegates it // input. In this case From delegates it to FromString, FromChannel and
// to FromString, FromChannel and FromIterable internally. // FromIterable internally.
func From(source interface{}) Query { func From(source interface{}) Query {
src := reflect.ValueOf(source) src := reflect.ValueOf(source)
@@ -84,8 +84,8 @@ func From(source interface{}) Query {
} }
} }
// FromChannel initializes a linq query with passed channel, // FromChannel initializes a linq query with passed channel, linq iterates over
// linq iterates over channel until it is closed. // channel until it is closed.
func FromChannel(source <-chan interface{}) Query { func FromChannel(source <-chan interface{}) Query {
return Query{ return Query{
Iterate: func() Iterator { Iterate: func() Iterator {
@@ -97,8 +97,8 @@ func FromChannel(source <-chan interface{}) Query {
} }
} }
// FromString initializes a linq query with passed string, // FromString initializes a linq query with passed string, linq iterates over
// linq iterates over runes of string. // runes of string.
func FromString(source string) Query { func FromString(source string) Query {
runes := []rune(source) runes := []rune(source)
len := len(runes) len := len(runes)
@@ -120,8 +120,8 @@ func FromString(source string) Query {
} }
} }
// FromIterable initializes a linq query with custom collection passed. // FromIterable initializes a linq query with custom collection passed. This
// This collection has to implement Iterable interface, linq iterates over items, // collection has to implement Iterable interface, linq iterates over items,
// that has to implement Comparable interface or be basic types. // that has to implement Comparable interface or be basic types.
func FromIterable(source Iterable) Query { func FromIterable(source Iterable) Query {
return Query{ return Query{
+41 -8
View File
@@ -6,14 +6,11 @@ type Group struct {
Group []interface{} Group []interface{}
} }
// GroupBy method groups the elements of a collection according // GroupBy method groups the elements of a collection according to a specified
// to a specified key selector function and projects the elements for each group // key selector function and projects the elements for each group by using a
// by using a specified function. // specified function.
func (q Query) GroupBy( func (q Query) GroupBy(keySelector func(interface{}) interface{},
keySelector func(interface{}) interface{}, elementSelector func(interface{}) interface{}) Query {
elementSelector func(interface{}) interface{},
) Query {
return Query{ return Query{
func() Iterator { func() Iterator {
next := q.Iterate() 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
View File
@@ -1,25 +1,27 @@
package linq package linq
import "reflect"
// GroupJoin correlates the elements of two collections based on key equality, // GroupJoin correlates the elements of two collections based on key equality,
// and groups the results. // and groups the results.
// //
// This method produces hierarchical results, which means that elements from outer query // This method produces hierarchical results, which means that elements from
// are paired with collections of matching elements from inner. GroupJoin enables you // outer query are paired with collections of matching elements from inner.
// to base your results on a whole set of matches for each element of outer query. // 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 // 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. // together with a collection of all the inner elements that match the outer
// This differs from the Join method, in which the result selector function is invoked // element. This differs from the Join method, in which the result selector
// on pairs that contain one element from outer and one element from inner. // 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, // GroupJoin preserves the order of the elements of outer, and for each element
// the order of the matching elements from inner. // of outer, the order of the matching elements from inner.
func (q Query) GroupJoin( func (q Query) GroupJoin(inner Query,
inner Query,
outerKeySelector func(interface{}) interface{}, outerKeySelector func(interface{}) interface{},
innerKeySelector func(interface{}) interface{}, innerKeySelector func(interface{}) interface{},
resultSelector func(outer interface{}, inners []interface{}) interface{}, resultSelector func(outer interface{}, inners []interface{}) interface{}) Query {
) Query {
return Query{ return Query{
Iterate: func() Iterator { 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
View File
@@ -2,8 +2,8 @@ package linq
// Intersect produces the set intersection of the source collection and the // 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 // 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, // the set that contains all the elements of A that also appear in B, but no
// but no other elements. // other elements.
func (q Query) Intersect(q2 Query) Query { func (q Query) Intersect(q2 Query) Query {
return Query{ return Query{
Iterate: func() Iterator { 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 // 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 // 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, // the set that contains all the elements of A that also appear in B, but no
// but no other elements. // other elements.
// //
// IntersectBy invokes a transform function on each element of both collections. // IntersectBy invokes a transform function on each element of both collections.
func (q Query) IntersectBy( func (q Query) IntersectBy(q2 Query,
q2 Query, selector func(interface{}) interface{}) Query {
selector func(interface{}) interface{},
) Query {
return Query{ return Query{
Iterate: func() Iterator { 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
View File
@@ -2,20 +2,18 @@ package linq
// Join correlates the elements of two collection based on matching keys. // Join correlates the elements of two collection based on matching keys.
// //
// A join refers to the operation of correlating the elements of two sources // A join refers to the operation of correlating the elements of two sources of
// of information based on a common key. Join brings the two information sources // 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. // and the keys by which they are matched together in one method call. This
// This differs from the use of SelectMany, which requires more than one method call // differs from the use of SelectMany, which requires more than one method call
// to perform the same operation. // to perform the same operation.
// //
// Join preserves the order of the elements of outer collection, // Join preserves the order of the elements of outer collection, and for each of
// and for each of these elements, the order of the matching elements of inner. // these elements, the order of the matching elements of inner.
func (q Query) Join( func (q Query) Join(inner Query,
inner Query,
outerKeySelector func(interface{}) interface{}, outerKeySelector func(interface{}) interface{},
innerKeySelector func(interface{}) interface{}, innerKeySelector func(interface{}) interface{},
resultSelector func(outer interface{}, inner interface{}) interface{}, resultSelector func(outer interface{}, inner interface{}) interface{}) Query {
) Query {
return Query{ return Query{
Iterate: func() Iterator { 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
View File
@@ -8,18 +8,17 @@ type order struct {
desc bool desc bool
} }
// OrderedQuery is the type returned from OrderBy, OrderByDescending // OrderedQuery is the type returned from OrderBy, OrderByDescending ThenBy and
// ThenBy and ThenByDescending functions. // ThenByDescending functions.
type OrderedQuery struct { type OrderedQuery struct {
Query Query
original Query original Query
orders []order orders []order
} }
// OrderBy sorts the elements of a collection in ascending order. // OrderBy sorts the elements of a collection in ascending order. Elements are
// Elements are sorted according to a key. // sorted according to a key.
func (q Query) OrderBy( func (q Query) OrderBy(selector func(interface{}) interface{}) OrderedQuery {
selector func(interface{}) interface{}) OrderedQuery {
return OrderedQuery{ return OrderedQuery{
orders: []order{{selector: selector}}, orders: []order{{selector: selector}},
original: q, 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. // OrderByDescending sorts the elements of a collection in descending order.
// Elements are sorted according to a key. // Elements are sorted according to a key.
func (q Query) OrderByDescending( func (q Query) OrderByDescending(selector func(interface{}) interface{}) OrderedQuery {
selector func(interface{}) interface{}) OrderedQuery {
return OrderedQuery{ return OrderedQuery{
orders: []order{{selector: selector, desc: true}}, orders: []order{{selector: selector, desc: true}},
original: q, original: q,
@@ -70,9 +89,28 @@ func (q Query) OrderByDescending(
} }
} }
// ThenBy performs a subsequent ordering of the elements in a collection // OrderByDescendingT is the typed version of OrderByDescending.
// in ascending order. This method enables you to specify multiple sort criteria // - selectorFn is of type "func(TSource) TKey"
// by applying any number of ThenBy or ThenByDescending methods. // 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( func (oq OrderedQuery) ThenBy(
selector func(interface{}) interface{}) OrderedQuery { selector func(interface{}) interface{}) OrderedQuery {
return OrderedQuery{ return OrderedQuery{
@@ -98,11 +136,29 @@ func (oq OrderedQuery) ThenBy(
} }
} }
// ThenByDescending performs a subsequent ordering of the elements in a collection // ThenByT is the typed version of ThenBy.
// in descending order. This method enables you to specify multiple sort criteria // - selectorFn is of type "func(TSource) TKey"
// by applying any number of ThenBy or ThenByDescending methods. // NOTE: ThenBy has better performance than ThenByT.
func (oq OrderedQuery) ThenByDescending( func (oq OrderedQuery) ThenByT(selectorFn interface{}) OrderedQuery {
selector func(interface{}) 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{ return OrderedQuery{
orders: append(oq.orders, order{selector: selector, desc: true}), orders: append(oq.orders, order{selector: selector, desc: true}),
original: oq.original, original: oq.original,
@@ -126,10 +182,32 @@ func (oq OrderedQuery) ThenByDescending(
} }
} }
// Sort returns a new query by sorting elements with provided less function // ThenByDescendingT is the typed version of ThenByDescending.
// in ascending order. The comparer function should return true if the parameter i // - selectorFn is of type "func(TSource) TKey"
// is less than j. While this method is uglier than chaining OrderBy, OrderByDescending, // NOTE: ThenByDescending has better performance than ThenByDescendingT.
// ThenBy and ThenByDescending methods, it's performance is much better. 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 { func (q Query) Sort(less func(i, j interface{}) bool) Query {
return Query{ return Query{
Iterate: func() Iterator { 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 { type sorter struct {
items []interface{} items []interface{}
less func(i, j interface{}) bool less func(i, j interface{}) bool
@@ -173,6 +270,10 @@ func (q Query) sort(orders []order) (r []interface{}) {
r = append(r, item) r = append(r, item)
} }
if len(r) == 0 {
return
}
for i, j := range orders { for i, j := range orders {
orders[i].compare = getComparer(j.selector(r[0])) orders[i].compare = getComparer(j.selector(r[0]))
} }
+297 -33
View File
@@ -18,6 +18,27 @@ func (q Query) All(predicate func(interface{}) bool) bool {
return true 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. // Any determines whether any element of a collection exists.
func (q Query) Any() bool { func (q Query) Any() bool {
_, ok := q.Iterate()() _, ok := q.Iterate()()
@@ -37,6 +58,28 @@ func (q Query) AnyWith(predicate func(interface{}) bool) bool {
return false 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. // Average computes the average of a collection of numeric values.
func (q Query) Average() (r float64) { func (q Query) Average() (r float64) {
next := q.Iterate() next := q.Iterate()
@@ -104,8 +147,8 @@ func (q Query) Count() (r int) {
return return
} }
// CountWith returns a number that represents how many elements // CountWith returns a number that represents how many elements in the specified
// in the specified collection satisfy a condition. // collection satisfy a condition.
func (q Query) CountWith(predicate func(interface{}) bool) (r int) { func (q Query) CountWith(predicate func(interface{}) bool) (r int) {
next := q.Iterate() next := q.Iterate()
@@ -118,14 +161,36 @@ func (q Query) CountWith(predicate func(interface{}) bool) (r int) {
return 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. // First returns the first element of a collection.
func (q Query) First() interface{} { func (q Query) First() interface{} {
item, _ := q.Iterate()() item, _ := q.Iterate()()
return item return item
} }
// FirstWith returns the first element of a collection that satisfies // FirstWith returns the first element of a collection that satisfies a
// a specified condition. // specified condition.
func (q Query) FirstWith(predicate func(interface{}) bool) interface{} { func (q Query) FirstWith(predicate func(interface{}) bool) interface{} {
next := q.Iterate() next := q.Iterate()
@@ -138,6 +203,99 @@ func (q Query) FirstWith(predicate func(interface{}) bool) interface{} {
return nil 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. // Last returns the last element of a collection.
func (q Query) Last() (r interface{}) { func (q Query) Last() (r interface{}) {
next := q.Iterate() next := q.Iterate()
@@ -149,8 +307,8 @@ func (q Query) Last() (r interface{}) {
return return
} }
// LastWith returns the last element of a collection that satisfies // LastWith returns the last element of a collection that satisfies a specified
// a specified condition. // condition.
func (q Query) LastWith(predicate func(interface{}) bool) (r interface{}) { func (q Query) LastWith(predicate func(interface{}) bool) (r interface{}) {
next := q.Iterate() next := q.Iterate()
@@ -163,6 +321,28 @@ func (q Query) LastWith(predicate func(interface{}) bool) (r interface{}) {
return 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. // Max returns the maximum value in a collection of values.
func (q Query) Max() (r interface{}) { func (q Query) Max() (r interface{}) {
next := q.Iterate() next := q.Iterate()
@@ -230,8 +410,8 @@ func (q Query) SequenceEqual(q2 Query) bool {
return !ok2 return !ok2
} }
// Single returns the only element of a collection, and nil // Single returns the only element of a collection, and nil if there is not
// if there is not exactly one element in the collection. // exactly one element in the collection.
func (q Query) Single() interface{} { func (q Query) Single() interface{} {
next := q.Iterate() next := q.Iterate()
item, ok := next() item, ok := next()
@@ -247,8 +427,8 @@ func (q Query) Single() interface{} {
return item return item
} }
// SingleWith returns the only element of a collection that satisfies // SingleWith returns the only element of a collection that satisfies a
// a specified condition, and nil if more than one such element exists. // specified condition, and nil if more than one such element exists.
func (q Query) SingleWith(predicate func(interface{}) bool) (r interface{}) { func (q Query) SingleWith(predicate func(interface{}) bool) (r interface{}) {
next := q.Iterate() next := q.Iterate()
found := false found := false
@@ -267,10 +447,31 @@ func (q Query) SingleWith(predicate func(interface{}) bool) (r interface{}) {
return 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. // SumInts computes the sum of a collection of numeric values.
// //
// Values can be of any integer type: int, int8, int16, int32, int64. // Values can be of any integer type: int, int8, int16, int32, int64. The result
// The result is int64. Method returns zero if collection contains no elements. // is int64. Method returns zero if collection contains no elements.
func (q Query) SumInts() (r int64) { func (q Query) SumInts() (r int64) {
next := q.Iterate() next := q.Iterate()
item, ok := next() item, ok := next()
@@ -290,8 +491,9 @@ func (q Query) SumInts() (r int64) {
// SumUInts computes the sum of a collection of numeric values. // SumUInts computes the sum of a collection of numeric values.
// //
// Values can be of any unsigned integer type: uint, uint8, uint16, uint32, uint64. // Values can be of any unsigned integer type: uint, uint8, uint16, uint32,
// The result is uint64. Method returns zero if collection contains no elements. // uint64. The result is uint64. Method returns zero if collection contains no
// elements.
func (q Query) SumUInts() (r uint64) { func (q Query) SumUInts() (r uint64) {
next := q.Iterate() next := q.Iterate()
item, ok := next() item, ok := next()
@@ -330,8 +532,8 @@ func (q Query) SumFloats() (r float64) {
return return
} }
// ToChannel iterates over a collection and outputs each element // ToChannel iterates over a collection and outputs each element to a channel,
// to a channel, then closes it. // then closes it.
func (q Query) ToChannel(result chan<- interface{}) { func (q Query) ToChannel(result chan<- interface{}) {
next := q.Iterate() 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. // ToMap iterates over a collection and populates result map with elements.
// Collection elements have to be of KeyValue type to use this method. // Collection elements have to be of KeyValue type to use this method. To
// To populate a map with elements of different type use ToMapBy method. // 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{}) { func (q Query) ToMap(result interface{}) {
q.ToMapBy( q.ToMapBy(
result, result,
@@ -356,15 +559,14 @@ func (q Query) ToMap(result interface{}) {
}) })
} }
// ToMapBy iterates over a collection and populates result map with elements. // ToMapBy iterates over a collection and populates the result map with
// Functions keySelector and valueSelector are executed for each element of the collection // elements. Functions keySelector and valueSelector are executed for each
// to generate key and value for the map. Generated key and value types must be assignable // element of the collection to generate key and value for the map. Generated
// to the map's key and value types. // key and value types must be assignable to the map's key and value types.
func (q Query) ToMapBy( // ToMapBy doesn't empty the result map before populating it.
result interface{}, func (q Query) ToMapBy(result interface{},
keySelector func(interface{}) interface{}, keySelector func(interface{}) interface{},
valueSelector func(interface{}) interface{}, valueSelector func(interface{}) interface{}) {
) {
res := reflect.ValueOf(result) res := reflect.ValueOf(result)
m := reflect.Indirect(res) m := reflect.Indirect(res)
next := q.Iterate() next := q.Iterate()
@@ -379,16 +581,78 @@ func (q Query) ToMapBy(
res.Elem().Set(m) res.Elem().Set(m)
} }
// ToSlice iterates over a collection and populates result slice with elements. // ToMapByT is the typed version of ToMapBy.
// Collection elements must be assignable to the slice's element type. //
func (q Query) ToSlice(result interface{}) { // - keySelectorFn is of type "func(TSource)TKey"
res := reflect.ValueOf(result) // - 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)
}
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) 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() next := q.Iterate()
index := 0
for item, ok := next(); ok; item, ok = next() { for item, ok := next(); ok; item, ok = next() {
slice = reflect.Append(slice, reflect.ValueOf(item)) if index >= cap {
slice, cap = grow(slice)
}
slice.Index(index).Set(reflect.ValueOf(item))
index++
} }
res.Elem().Set(slice) // 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
View File
@@ -2,9 +2,9 @@ package linq
// Reverse inverts the order of the elements in a collection. // Reverse inverts the order of the elements in a collection.
// //
// Unlike OrderBy, this sorting method does not consider the actual values themselves // Unlike OrderBy, this sorting method does not consider the actual values
// in determining the order. Rather, it just returns the elements in the reverse order // themselves in determining the order. Rather, it just returns the elements in
// from which they are produced by the underlying source. // the reverse order from which they are produced by the underlying source.
func (q Query) Reverse() Query { func (q Query) Reverse() Query {
return Query{ return Query{
Iterate: func() Iterator { Iterate: func() Iterator {
+65 -27
View File
@@ -1,17 +1,16 @@
package linq package linq
// Select projects each element of a collection into a new form. // Select projects each element of a collection into a new form. Returns a query
// Returns a query with the result of invoking the transform function // with the result of invoking the transform function on each element of
// on each element of original source. // original source.
// //
// This projection method requires the transform function, selector, // This projection method requires the transform function, selector, to produce
// to produce one value for each value in the source collection. // one value for each value in the source collection. If selector returns a
// If selector returns a value that is itself a collection, // value that is itself a collection, it is up to the consumer to traverse the
// it is up to the consumer to traverse the subcollections manually. // subcollections manually. In such a situation, it might be better for your
// In such a situation, it might be better for your query to return a single // query to return a single coalesced collection of values. To achieve this, use
// coalesced collection of values. To achieve this, use the SelectMany method // the SelectMany method instead of Select. Although SelectMany works similarly
// instead of Select. Although SelectMany works similarly to Select, // to Select, it differs in that the transform function returns a collection
// it differs in that the transform function returns a collection
// that is then expanded by SelectMany before it is returned. // that is then expanded by SelectMany before it is returned.
func (q Query) Select(selector func(interface{}) interface{}) Query { func (q Query) Select(selector func(interface{}) interface{}) Query {
return 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 // SelectT is the typed version of Select.
// by incorporating the element's index. Returns a query with the result // - selectorFn is of type "func(TSource)TResult"
// of invoking the transform function on each element of original source. // 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 // The first argument to selector represents the zero-based index of that
// in the source collection. This can be useful if the elements are in a known order // element in the source collection. This can be useful if the elements are in a
// and you want to do something with an element at a particular index, // known order and you want to do something with an element at a particular
// for example. It can also be useful if you want to retrieve the index of one // index, for example. It can also be useful if you want to retrieve the index
// or more elements. The second argument to selector represents the element to process. // of one or more elements. The second argument to selector represents the
// element to process.
// //
// This projection method requires the transform function, selector, // This projection method requires the transform function, selector, to produce
// to produce one value for each value in the source collection. // one value for each value in the source collection. If selector returns a
// If selector returns a value that is itself a collection, // value that is itself a collection, it is up to the consumer to traverse the
// it is up to the consumer to traverse the subcollections manually. // subcollections manually. In such a situation, it might be better for your
// In such a situation, it might be better for your query to return a single // query to return a single coalesced collection of values. To achieve this, use
// coalesced collection of values. To achieve this, use the SelectMany method // the SelectMany method instead of Select. Although SelectMany works similarly
// instead of Select. Although SelectMany works similarly to Select, // to Select, it differs in that the transform function returns a collection
// it differs in that the transform function returns a collection
// that is then expanded by SelectMany before it is returned. // that is then expanded by SelectMany before it is returned.
func (q Query) SelectIndexed(selector func(int, interface{}) interface{}) Query { func (q Query) SelectIndexed(selector func(int, interface{}) interface{}) Query {
return 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
View File
@@ -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 // SelectManyT is the typed version of SelectMany.
// flattens the resulting collection into one collection.
// //
// The first argument to selector represents the zero-based index of that element // - selectorFn is of type "func(TSource)Query"
// 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. // NOTE: SelectMany has better performance than SelectManyT.
// It can also be useful if you want to retrieve the index of one or more elements. func (q Query) SelectManyT(selectorFn interface{}) Query {
// The second argument to selector represents the element to process.
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 { func (q Query) SelectManyIndexed(selector func(int, interface{}) Query) Query {
return Query{ return Query{
Iterate: func() Iterator { 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 // SelectManyBy projects each element of a collection to a Query, iterates and
// flattens the resulting collection into one collection, and invokes // flattens the resulting collection into one collection, and invokes a result
// a result selector function on each element therein. // selector function on each element therein.
func (q Query) SelectManyBy( func (q Query) SelectManyBy(selector func(interface{}) Query,
selector func(interface{}) Query, resultSelector func(interface{}, interface{}) interface{}) Query {
resultSelector func(interface{}, interface{}) interface{},
) Query {
return Query{ return Query{
Iterate: func() Iterator { Iterate: func() Iterator {
@@ -103,18 +146,53 @@ func (q Query) SelectManyBy(
} }
} }
item = resultSelector(outer, item) item = resultSelector(item, outer)
return return
} }
}, },
} }
} }
// SelectManyByIndexed projects each element of a collection to a Query, iterates and // SelectManyByT is the typed version of SelectManyBy.
// flattens the resulting collection into one collection, and invokes //
// a result selector function on each element therein. // - selectorFn is of type "func(TSource)Query"
// The index of each source element is used in the intermediate projected form // - resultSelectorFn is of type "func(TSource,TCollection)TResult"
// of that element. //
// 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, func (q Query) SelectManyByIndexed(selector func(int, interface{}) Query,
resultSelector func(interface{}, interface{}) 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 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
View File
@@ -1,7 +1,7 @@
package linq package linq
// Skip bypasses a specified number of elements in a collection // Skip bypasses a specified number of elements in a collection and then returns
// and then returns the remaining elements. // the remaining elements.
func (q Query) Skip(count int) Query { func (q Query) Skip(count int) Query {
return Query{ return Query{
Iterate: func() Iterator { 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 // SkipWhile bypasses elements in a collection as long as a specified condition
// and then returns the remaining elements. // is true and then returns the remaining elements.
// //
// This method tests each element by using predicate and skips the element // This method tests each element by using predicate and skips the element if
// if the result is true. After the predicate function returns false for an element, // the result is true. After the predicate function returns false for an
// that element and the remaining elements in source are returned // element, that element and the remaining elements in source are returned and
// and there are no more invocations of predicate. // there are no more invocations of predicate.
func (q Query) SkipWhile(predicate func(interface{}) bool) Query { func (q Query) SkipWhile(predicate func(interface{}) bool) Query {
return Query{ return Query{
Iterate: func() Iterator { 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 // SkipWhileT is the typed version of SkipWhile.
// 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 // - predicateFn is of type "func(TSource)bool"
// if the result is true. After the predicate function returns false for an element, //
// that element and the remaining elements in source are returned // NOTE: SkipWhile has better performance than SkipWhileT.
// and there are no more invocations of predicate. 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 { func (q Query) SkipWhileIndexed(predicate func(int, interface{}) bool) Query {
return Query{ return Query{
Iterate: func() Iterator { 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
View File
@@ -1,6 +1,7 @@
package linq 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 { func (q Query) Take(count int) Query {
return Query{ return Query{
Iterate: func() Iterator { 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, // TakeWhile returns elements from a collection as long as a specified condition
// and then skips the remaining elements. // is true, and then skips the remaining elements.
func (q Query) TakeWhile(predicate func(interface{}) bool) Query { func (q Query) TakeWhile(predicate func(interface{}) bool) Query {
return Query{ return Query{
Iterate: func() Iterator { 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 // TakeWhileT is the typed version of TakeWhile.
// 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 // - predicateFn is of type "func(TSource)bool"
// within collection. The second argument represents the element to test. //
// 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 { func (q Query) TakeWhileIndexed(predicate func(int, interface{}) bool) Query {
return Query{ return Query{
Iterate: func() Iterator { 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
View File
@@ -2,10 +2,9 @@ package linq
// Union produces the set union of two collections. // Union produces the set union of two collections.
// //
// This method excludes duplicates from the return set. // This method excludes duplicates from the return set. This is different
// This is different behavior to the Concat method, // behavior to the Concat method, which returns all the elements in the input
// which returns all the elements in the input collection // collection including duplicates.
// including duplicates.
func (q Query) Union(q2 Query) Query { func (q Query) Union(q2 Query) Query {
return Query{ return Query{
Iterate: func() Iterator { Iterate: func() Iterator {
+47 -4
View File
@@ -19,11 +19,33 @@ func (q Query) Where(predicate func(interface{}) bool) Query {
} }
} }
// WhereIndexed filters a collection of values based on a predicate. // WhereT is the typed version of Where.
// 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. // - predicateFn is of type "func(TSource)bool"
// The second argument of predicate represents the element to test. //
// 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 { func (q Query) WhereIndexed(predicate func(int, interface{}) bool) Query {
return Query{ return Query{
Iterate: func() Iterator { 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
View File
@@ -1,19 +1,17 @@
package linq package linq
// Zip applies a specified function to the corresponding elements // Zip applies a specified function to the corresponding elements of two
// of two collections, producing a collection of the results. // collections, producing a collection of the results.
// //
// The method steps through the two input collections, applying function // The method steps through the two input collections, applying function
// resultSelector to corresponding elements of the two collections. // resultSelector to corresponding elements of the two collections. The method
// The method returns a collection of the values that are returned by resultSelector. // returns a collection of the values that are returned by resultSelector. If
// If the input collections do not have the same number of elements, // the input collections do not have the same number of elements, the method
// the method combines elements until it reaches the end of one of the collections. // combines elements until it reaches the end of one of the collections. For
// For example, if one collection has three elements and the other one has four, // example, if one collection has three elements and the other one has four, the
// the result collection has only three elements. // result collection has only three elements.
func (q Query) Zip( func (q Query) Zip(q2 Query,
q2 Query, resultSelector func(interface{}, interface{}) interface{}) Query {
resultSelector func(interface{}, interface{}) interface{},
) Query {
return Query{ return Query{
Iterate: func() Iterator { 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)
}
+3 -1
View File
@@ -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/ahmetb/go-linq
# github.com/go-ini/ini v1.57.0 # github.com/go-ini/ini v1.57.0
github.com/go-ini/ini github.com/go-ini/ini
@@ -8,3 +8,5 @@ github.com/json-iterator/go
github.com/modern-go/concurrent github.com/modern-go/concurrent
# github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 # github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742
github.com/modern-go/reflect2 github.com/modern-go/reflect2
# gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v2