From 2ac2b2666f6994c0e03cbdf8b2a07ef2cd785155 Mon Sep 17 00:00:00 2001 From: Huangzj Date: Thu, 12 Nov 2020 18:10:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(Go-Tool)=EF=BC=9A2020/11/12:=E6=96=B0?= =?UTF-8?q?=E5=A2=9Elinq=E5=8C=85=E4=BD=BF=E7=94=A8=E7=A4=BA=E4=BE=8B(?= =?UTF-8?q?=E6=9C=AA=E5=AE=8C=E6=88=90)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 + go.sum | 4 + readme.md | 5 + util/linqUse/DataInit.go | 71 ++++ util/linqUse/FuncSet.go | 41 ++ util/linqUse/LinqUse_test.go | 104 +++++ .../ahmetalpbalkan/go-linq/.gitignore | 29 ++ .../ahmetalpbalkan/go-linq/.travis.yml | 20 + .../github.com/ahmetalpbalkan/go-linq/LICENSE | 14 + .../ahmetalpbalkan/go-linq/README.md | 141 +++++++ .../ahmetalpbalkan/go-linq/aggregate.go | 53 +++ .../ahmetalpbalkan/go-linq/compare.go | 200 +++++++++ .../ahmetalpbalkan/go-linq/concat.go | 74 ++++ .../ahmetalpbalkan/go-linq/convert.go | 70 ++++ .../ahmetalpbalkan/go-linq/distinct.go | 75 ++++ .../github.com/ahmetalpbalkan/go-linq/doc.go | 5 + .../ahmetalpbalkan/go-linq/except.go | 59 +++ .../github.com/ahmetalpbalkan/go-linq/from.go | 172 ++++++++ .../ahmetalpbalkan/go-linq/groupby.go | 48 +++ .../ahmetalpbalkan/go-linq/groupjoin.go | 50 +++ .../ahmetalpbalkan/go-linq/intersect.go | 67 +++ .../github.com/ahmetalpbalkan/go-linq/join.go | 56 +++ .../ahmetalpbalkan/go-linq/orderby.go | 212 ++++++++++ .../ahmetalpbalkan/go-linq/result.go | 394 ++++++++++++++++++ .../ahmetalpbalkan/go-linq/reverse.go | 30 ++ .../ahmetalpbalkan/go-linq/select.go | 71 ++++ .../ahmetalpbalkan/go-linq/selectmany.go | 151 +++++++ .../github.com/ahmetalpbalkan/go-linq/skip.go | 91 ++++ .../github.com/ahmetalpbalkan/go-linq/take.go | 84 ++++ .../ahmetalpbalkan/go-linq/union.go | 41 ++ .../ahmetalpbalkan/go-linq/where.go | 46 ++ .../github.com/ahmetalpbalkan/go-linq/zip.go | 35 ++ vendor/modules.txt | 2 + 33 files changed, 2517 insertions(+) create mode 100644 util/linqUse/DataInit.go create mode 100644 util/linqUse/FuncSet.go create mode 100644 util/linqUse/LinqUse_test.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/.gitignore create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/.travis.yml create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/LICENSE create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/README.md create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/aggregate.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/compare.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/concat.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/convert.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/distinct.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/doc.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/except.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/from.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/groupby.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/groupjoin.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/intersect.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/join.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/orderby.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/result.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/reverse.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/select.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/selectmany.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/skip.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/take.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/union.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/where.go create mode 100644 vendor/github.com/ahmetalpbalkan/go-linq/zip.go diff --git a/go.mod b/go.mod index 47a06be..7855d5e 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.13 require ( github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 // indirect + github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible github.com/davecgh/go-spew v1.1.1 // indirect github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 // indirect github.com/go-ini/ini v1.57.0 @@ -13,5 +14,6 @@ require ( 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/ini.v1 v1.62.0 // indirect gopkg.in/yaml.v2 v2.2.8 // indirect ) diff --git a/go.sum b/go.sum index 4a98622..c4f711e 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75 h1:xGHheKK44eC6K0u5X+DZW/fRaR1LnDdqPHMZMWx5fv8= github.com/Bowery/prompt v0.0.0-20190916142128-fa8279994f75/go.mod h1:4/6eNcqZ09BZ9wLK3tZOjBA1nDj+B0728nlX5YRlSmQ= +github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible h1:8q+6K5jJ2RCTD7v7VxMfuUdHyU22GZi+qMY0hpLTnVc= +github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible/go.mod h1:IBvGwXyZVvXuN8KIbGCK53k+fzCKOmAdYhobMdJXOck= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -43,6 +45,8 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T 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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= +gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/readme.md b/readme.md index 7d8cc00..e05f799 100644 --- a/readme.md +++ b/readme.md @@ -54,6 +54,8 @@ 2020/11/9: 新增lomute划分算法效率单测,结果:多次查找,元素多的情况下由末尾开始查找效率更高 +2020/11/12:新增linq包使用示例(未完成) + # mod vendor模式加载包 通过go mod的方式加载的github上面的包会有报红的问题,但是包本身是可以运行的,这样就是会有一个问题,如果你想要点击去看方法的内容,没办法做到 @@ -64,4 +66,7 @@ 处理步骤如下 : - 通过 go get github.com/kardianos/govendor 命令下载govendor命令 +- 把vendor文件夹删除了 - 通过 go mod vendor 切换到vendor管理 +- 在文件中import引用对应的路径 +- vendor加载对应的包 \ No newline at end of file diff --git a/util/linqUse/DataInit.go b/util/linqUse/DataInit.go new file mode 100644 index 0000000..4a00f99 --- /dev/null +++ b/util/linqUse/DataInit.go @@ -0,0 +1,71 @@ +/* + * @Author : huangzj + * @Time : 2020/11/12 14:04 + * @Description: + */ + +package linqUse + +import "time" + +type Book struct { + Name string + Author string + Money float64 + WordsNum int + PublishTime time.Time +} + +func MakeBook() []Book { + bookList := make([]Book, 0) + bookList = append(bookList, Book{ + Name: "Go语言", + Author: "Go", + Money: 100, + WordsNum: 1000, + PublishTime: time.Date(2020, 1, 1, 10, 0, 0, 0, time.Local), + }) + bookList = append(bookList, Book{ + Name: "Effective Java", + Author: "Java", + Money: 78, + WordsNum: 9000, + PublishTime: time.Date(2020, 2, 15, 10, 0, 0, 0, time.Local), + }) + bookList = append(bookList, Book{ + Name: "Java语言", + Author: "Java", + Money: 50, + WordsNum: 3000, + PublishTime: time.Date(2020, 2, 1, 10, 0, 0, 0, time.Local), + }) + bookList = append(bookList, Book{ + Name: "Lua语言", + Author: "Lua", + Money: 75, + WordsNum: 45000, + PublishTime: time.Date(2020, 1, 10, 10, 0, 0, 0, time.Local), + }) + bookList = append(bookList, Book{ + Name: "React语言", + Author: "React", + Money: 99, + WordsNum: 14500, + PublishTime: time.Date(2020, 7, 1, 10, 0, 0, 0, time.Local), + }) + bookList = append(bookList, Book{ + Name: "Red语言", + Author: "Red", + Money: 28, + WordsNum: 880, + PublishTime: time.Date(2019, 4, 1, 10, 0, 0, 0, time.Local), + }) + bookList = append(bookList, Book{ + Name: "JavaScript语言", + Author: "JavaScript", + Money: 81, + WordsNum: 3776, + PublishTime: time.Date(2019, 5, 17, 10, 0, 0, 0, time.Local), + }) + return bookList +} diff --git a/util/linqUse/FuncSet.go b/util/linqUse/FuncSet.go new file mode 100644 index 0000000..c4fddb2 --- /dev/null +++ b/util/linqUse/FuncSet.go @@ -0,0 +1,41 @@ +/* + * @Author : huangzj + * @Time : 2020/11/12 14:43 + * @Description:测试方法集合 + */ + +package linqUse + +import "time" + +//书籍发布时间是否早于--.--.-- +var PublishTimeBeforeFunc = func(thisBook interface{}) bool { + if thisBook.(Book).PublishTime.Before(time.Date(2020, 1, 1, 0, 0, 0, 0, time.Local)) { + return true + } + return false +} + +//书籍发布时间是否晚于--.--.-- +var PublishTimeAfterFunc = func(thisBook interface{}) bool { + if thisBook.(Book).PublishTime.After(time.Date(2018, 1, 1, 0, 0, 0, 0, time.Local)) { + return true + } + return false +} + +//书籍发布时间是否晚于--.--.-- +var PublishTimeAfterFunc2 = func(thisBook interface{}) bool { + if thisBook.(Book).PublishTime.After(time.Date(2021, 1, 1, 0, 0, 0, 0, time.Local)) { + return true + } + return false +} + +//自定义聚合操作方法 +var AggregateFunc = func(first interface{}, second interface{}) interface{} { + if first.(Book).PublishTime.Before(second.(*Book).PublishTime) { + return first + } + return second +} diff --git a/util/linqUse/LinqUse_test.go b/util/linqUse/LinqUse_test.go new file mode 100644 index 0000000..e652d85 --- /dev/null +++ b/util/linqUse/LinqUse_test.go @@ -0,0 +1,104 @@ +/* + * @Author : huangzj + * @Time : 2020/11/12 10:52 + * @Description:这边是linq包的使用操作,为之后的使用做一些示例 + * 包引用命令: go get github.com/ahmetalpbalkan/go-linq + */ + +package linqUse + +import ( + "Go-Tool/util/timed" + "fmt" + "github.com/ahmetalpbalkan/go-linq" + "testing" + "time" +) + +func Test(t *testing.T) { + bookList := MakeBook() + query := linq.From(bookList) + + //Aggregate(query) //自定义聚合操作 + // + //All(query) //判断数组的所有元素是否都满足条件 + // + //AnyWith(query) //判断数组是否有任意个元素满足条件 + + //Calculate() //这个方法只能计算数值类型 + + //Combine(query) //组合两个数组的两种方式 + + //fmt.Println(fmt.Sprintf("跳过三个元素,返回的数组中包含%d个元素", len(query.Skip(3).Results()))) + //fmt.Println(fmt.Sprintf("跳过发布时间在2020.1.1之前的元素,返回的数组中包含%d个元素", len(query.SkipWhile(PublishTimeBeforeFunc).Results()))) + + //Contains(query) //判断数组中是否包含某个元素 + //query.CountWith() + // + //query.Distinct() + //query.DistinctBy() + // + //query.OrderBy() + //query.OrderByDescending() + + //FirstAndLast(query) //获取第一个/最后一个元素 + +} + +func Combine(query linq.Query) { + query1 := query.Concat(query) //不会排除重复项 + query2 := query.Union(query) //会排除重复项 + fmt.Println(fmt.Sprintf("不会排除重复项的方法返回的元素个数是%d", len(query1.Results()))) + fmt.Println(fmt.Sprintf("会排除重复项的方法返回的元素个数是%d", len(query2.Results()))) +} + +func FirstAndLast(query linq.Query) { + fmt.Println(fmt.Sprintf("数组第一本书书名叫:%s", query.First().(Book).Name)) + fmt.Println(fmt.Sprintf("数组第一本书发布时间在2020年1月1号之前的书书名叫:%s", query.FirstWith(PublishTimeBeforeFunc).(Book).Name)) + + fmt.Println(fmt.Sprintf("数组最后一本书书名叫:%s", query.Last().(Book).Name)) + fmt.Println(fmt.Sprintf("数组最后一本书发布时间在2020年1月1号之前的书书名叫:%s", query.LastWith(PublishTimeBeforeFunc).(Book).Name)) +} + +func Contains(query linq.Query) { + //这边需要注意是的是如果要判断两个元素是否相等,在go里面不能使用元素的地址 + // 所以这边加到query里面的必须不能是元素的地址,否则没办法比较结构体本身,而是比较结构体的地址 + fmt.Println(fmt.Sprintf("判断当前数组是否包含相同的元素,判断的是所有的值,而不是地址,结果是%v", query.Contains(Book{ + Name: "Go语言", + Author: "Go", + Money: 100, + WordsNum: 1000, + PublishTime: time.Date(2020, 1, 1, 10, 0, 0, 0, time.Local), + }))) +} + +func Calculate() { + query := linq.From([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}) + fmt.Println(fmt.Sprintf("平均值为:%f", query.Average())) + fmt.Println(fmt.Sprintf("最大的值为%d", query.Max())) + fmt.Println(fmt.Sprintf("最小的值为%d", query.Min())) +} + +func AnyWith(query linq.Query) { + result := query.AnyWith(PublishTimeBeforeFunc) //判断是否有任意个元素满足条件 + fmt.Println(fmt.Sprintf("是否有任意书的发表时间早于2020年1月1号,答案是%v", result)) + + result = query.AnyWith(PublishTimeAfterFunc2) //判断是否有任意个元素满足条件 + fmt.Println(fmt.Sprintf("是否有任意书的发表时间晚于2021年1月1号,答案是%v", result)) +} + +func All(query linq.Query) { + result := query.All(PublishTimeBeforeFunc) + fmt.Println(fmt.Sprintf("是否所有的书的发表时间都早于2020年1月1号,答案是%v", result)) + + result = query.All(PublishTimeAfterFunc) + fmt.Println(fmt.Sprintf("是否所有的书的发表时间都晚于2018年1月1号,答案是%v", result)) +} + +func Aggregate(query linq.Query) { + // 这边的聚合操作其实就是【自定义对应的比较方法】。 + // 根据注释,第一次拿到的是数组的第一个元素,然后跟第二个元素进行比较,如果第二个元素符合条件则【替换第一个元素】,否则当前聚合操作还是持有第一个元素。 + // 以此类推后面的元素 + result := query.Aggregate(AggregateFunc).(*Book) + fmt.Println(fmt.Sprintf("聚合操作查询出的结果是【%s】这本书,它的发布时间最早为:%v", result.Name, timed.GetTimeDefaultFormatString(result.PublishTime.Unix()))) +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/.gitignore b/vendor/github.com/ahmetalpbalkan/go-linq/.gitignore new file mode 100644 index 0000000..6de9458 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/.gitignore @@ -0,0 +1,29 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +### Code ### +# Visual Studio Code - https://code.visualstudio.com/ +.settings/ +.vscode/ \ No newline at end of file diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/.travis.yml b/vendor/github.com/ahmetalpbalkan/go-linq/.travis.yml new file mode 100644 index 0000000..6587753 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/.travis.yml @@ -0,0 +1,20 @@ +sudo: false + +language: go +go: + - 1.5 + - 1.7 + +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover + - go get -u github.com/golang/lint/golint + +script: + - go vet -x ./... + - golint ./... + - go test -v ./... + - go test -covermode=count -coverprofile=profile.cov + +after_script: + - goveralls -coverprofile=profile.cov -service=travis-ci \ No newline at end of file diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/LICENSE b/vendor/github.com/ahmetalpbalkan/go-linq/LICENSE new file mode 100644 index 0000000..71dd824 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/LICENSE @@ -0,0 +1,14 @@ +Copyright 2016 Ahmet Alp Balkan + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/README.md b/vendor/github.com/ahmetalpbalkan/go-linq/README.md new file mode 100644 index 0000000..475464c --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/README.md @@ -0,0 +1,141 @@ +# 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. +* Written in vanilla Go! +* Safe for concurrent use +* Complete lazy evaluation with iterator pattern +* Supports arrays, slices, maps, strings, channels and custom collections +(collection needs to implement `Iterable` interface and element - `Comparable` +interface) + +## Installation + + $ go get github.com/ahmetalpbalkan/go-linq + +> :warning: :warning: `go-linq` has recently introduced _breaking API changes_ +> with v2.0.0. See [release notes](#release-notes) for details. v2.0.0 comes with +> a refined interface, dramatically increased performance and memory efficiency, +> and new features such as lazy evaluation ([read more](http://kalan.rocks/2016/07/16/manipulating-data-with-iterators-in-go/)). +> +> The old version is still available in `archive/0.9` branch and tagged as `0.9` +> as well. If you are using `go-linq`, please vendor a copy of it in your +> source tree to avoid getting broken by upstream changes. + +## Quickstart + +Usage is as easy as chaining methods like: + +`From(slice)` `.Where(predicate)` `.Select(selector)` `.Union(data)` + +**Example: Find all owners of cars manufactured from 2015** +```go +import . "github.com/ahmetalpbalkan/go-linq" + +type Car struct { + id, year int + owner, model string +} + +owners := []string{} + +From(cars).Where(func(c interface{}) bool { + return c.(Car).year >= 2015 +}).Select(func(c interface{}) interface{} { + return c.(Car).owner +}).ToSlice(&owners) +``` + +**Example: Find the author who has written the most books** +```go +import . "github.com/ahmetalpbalkan/go-linq" + +type Book struct { + id int + title string + authors []string +} + +author := From(books).SelectMany( // make a flat array of authors + func(book interface{}) Query { + return From(book.(Book).authors) + }).GroupBy( // group by author + func(author interface{}) interface{} { + return author // author as key + }, func(author interface{}) interface{} { + return author // author as value + }).OrderByDescending( // sort groups by its length + func(group interface{}) interface{} { + return len(group.(Group).Group) + }).Select( // get authors out of groups + func(group interface{}) interface{} { + return group.(Group).Key + }).First() // take the first author +``` + +**Example: Implement a custom method that leaves only values greater than the specified threshold** +```go +type MyQuery Query + +func (q MyQuery) GreaterThan(threshold int) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + + return func() (item interface{}, ok bool) { + for item, ok = next(); ok; item, ok = next() { + if item.(int) > threshold { + return + } + } + + return + } + }, + } +} + +result := MyQuery(Range(1,10)).GreaterThan(5).Results() +``` + +**More examples** can be found in [documentation](https://godoc.org/github.com/ahmetalpbalkan/go-linq). + +## Release Notes +~~~ + +v2.0.0 (2016-09-02) +* IMPORTANT: This release is a BREAKING CHANGE. The old version + is archived at the 'archive/0.9' branch or the 0.9 tags. +* A COMPLETE REWRITE of go-linq with better performance and memory + efficiency. (thanks @kalaninja!) +* API has significantly changed. Most notably: + - linq.T removed in favor of interface{} + - library methods no longer return errors + - PLINQ removed for now (see channels support) + - support for channels, custom collections and comparables + +v0.9-rc4 +* GroupBy() + +v0.9-rc3.2 +* bugfix: All() iterating over values instead of indices + +v0.9-rc3.1 +* bugfix: modifying result slice affects subsequent query methods + +v0.9-rc3 +* removed FirstOrNil, LastOrNil, ElementAtOrNil methods + +v0.9-rc2.5 +* slice-accepting methods accept slices of any type with reflections + +v0.9-rc2 +* parallel linq (plinq) implemented +* Queryable separated into Query & ParallelQuery +* fixed early termination for All + +v0.9-rc1 +* many linq methods are implemented +* methods have error handling support +* type assertion limitations are unresolved +* travis-ci.org build integrated +* open sourced on github, master & dev branches +~~~ diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/aggregate.go b/vendor/github.com/ahmetalpbalkan/go-linq/aggregate.go new file mode 100644 index 0000000..a8658c5 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/aggregate.go @@ -0,0 +1,53 @@ +package linq + +// Aggregate applies an accumulator function over a sequence. +// +// Aggregate method makes it simple to perform a calculation over a sequence of values. +// This method works by calling f() one time for each element in source +// except the first one. Each time f() is called, Aggregate passes both +// the element from the sequence and an aggregated value (as the first argument to f()). +// The first element of source is used as the initial aggregate value. +// The result of f() replaces the previous aggregated value. +// +// Aggregate returns the final result of f(). +func (q Query) Aggregate( + f func(interface{}, interface{}) interface{}) interface{} { + next := q.Iterate() + + result, any := next() + if !any { + return nil + } + + for current, ok := next(); ok; current, ok = next() { + result = f(result, current) + } + + return result +} + +// AggregateWithSeed applies an accumulator function over a sequence. +// The specified seed value is used as the initial accumulator value. +// +// Aggregate method makes it simple to perform a calculation over a sequence of values. +// This method works by calling f() one time for each element in source +// except the first one. Each time f() is called, Aggregate passes both +// the element from the sequence and an aggregated value (as the first argument to f()). +// The value of the seed parameter is used as the initial aggregate value. +// The result of f() replaces the previous aggregated value. +// +// Aggregate returns the final result of f(). +func (q Query) AggregateWithSeed( + seed interface{}, + f func(interface{}, interface{}) interface{}, +) interface{} { + + next := q.Iterate() + result := seed + + for current, ok := next(); ok; current, ok = next() { + result = f(result, current) + } + + return result +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/compare.go b/vendor/github.com/ahmetalpbalkan/go-linq/compare.go new file mode 100644 index 0000000..c78e4aa --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/compare.go @@ -0,0 +1,200 @@ +package linq + +type comparer func(interface{}, interface{}) int + +// Comparable is an interface that has to be implemented by a +// custom collection elememts in order to work with linq. +// +// Example: +// func (f foo) CompareTo(c Comparable) int { +// a, b := f.f1, c.(foo).f1 +// +// if a < b { +// return -1 +// } else if a > b { +// return 1 +// } +// +// return 0 +// } +type Comparable interface { + CompareTo(Comparable) int +} + +func getComparer(data interface{}) comparer { + switch data.(type) { + case int: + return func(x, y interface{}) int { + a, b := x.(int), y.(int) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case int8: + return func(x, y interface{}) int { + a, b := x.(int8), y.(int8) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case int16: + return func(x, y interface{}) int { + a, b := x.(int16), y.(int16) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case int32: + return func(x, y interface{}) int { + a, b := x.(int32), y.(int32) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case int64: + return func(x, y interface{}) int { + a, b := x.(int64), y.(int64) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case uint: + return func(x, y interface{}) int { + a, b := x.(uint), y.(uint) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case uint8: + return func(x, y interface{}) int { + a, b := x.(uint8), y.(uint8) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case uint16: + return func(x, y interface{}) int { + a, b := x.(uint16), y.(uint16) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case uint32: + return func(x, y interface{}) int { + a, b := x.(uint32), y.(uint32) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case uint64: + return func(x, y interface{}) int { + a, b := x.(uint64), y.(uint64) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case float32: + return func(x, y interface{}) int { + a, b := x.(float32), y.(float32) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case float64: + return func(x, y interface{}) int { + a, b := x.(float64), y.(float64) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case string: + return func(x, y interface{}) int { + a, b := x.(string), y.(string) + switch { + case a > b: + return 1 + case b > a: + return -1 + default: + return 0 + } + } + case bool: + return func(x, y interface{}) int { + a, b := x.(bool), y.(bool) + switch { + case a == b: + return 0 + case a: + return 1 + default: + return -1 + } + } + default: + return func(x, y interface{}) int { + a, b := x.(Comparable), y.(Comparable) + return a.CompareTo(b) + } + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/concat.go b/vendor/github.com/ahmetalpbalkan/go-linq/concat.go new file mode 100644 index 0000000..6210126 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/concat.go @@ -0,0 +1,74 @@ +package linq + +// Append inserts an item to the end of a collection, +// so it becomes the last item. +func (q Query) Append(item interface{}) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + appended := false + + return func() (interface{}, bool) { + i, ok := next() + if ok { + return i, ok + } + + if !appended { + appended = true + return item, true + } + + return nil, false + } + }, + } +} + +// Concat concatenates two collections. +// +// The Concat method differs from the Union method because the Concat method +// returns all the original elements in the input sequences. +// The Union method returns only unique elements. +func (q Query) Concat(q2 Query) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + next2 := q2.Iterate() + use1 := true + + return func() (item interface{}, ok bool) { + if use1 { + item, ok = next() + if ok { + return + } + + use1 = false + } + + return next2() + } + }, + } +} + +// Prepend inserts an item to the beginning of a collection, +// so it becomes the first item. +func (q Query) Prepend(item interface{}) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + prepended := false + + return func() (interface{}, bool) { + if prepended { + return next() + } + + prepended = true + return item, true + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/convert.go b/vendor/github.com/ahmetalpbalkan/go-linq/convert.go new file mode 100644 index 0000000..065175f --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/convert.go @@ -0,0 +1,70 @@ +package linq + +type intConverter func(interface{}) int64 + +func getIntConverter(data interface{}) intConverter { + switch data.(type) { + case (int): + return func(i interface{}) int64 { + return int64(i.(int)) + } + case (int8): + return func(i interface{}) int64 { + return int64(i.(int8)) + } + case (int16): + return func(i interface{}) int64 { + return int64(i.(int16)) + } + case (int32): + return func(i interface{}) int64 { + return int64(i.(int32)) + } + } + + return func(i interface{}) int64 { + return i.(int64) + } +} + +type uintConverter func(interface{}) uint64 + +func getUIntConverter(data interface{}) uintConverter { + switch data.(type) { + case (uint): + return func(i interface{}) uint64 { + return uint64(i.(uint)) + } + case (uint8): + return func(i interface{}) uint64 { + return uint64(i.(uint8)) + } + case (uint16): + return func(i interface{}) uint64 { + return uint64(i.(uint16)) + } + case (uint32): + return func(i interface{}) uint64 { + return uint64(i.(uint32)) + } + } + + return func(i interface{}) uint64 { + return i.(uint64) + } +} + +type floatConverter func(interface{}) float64 + +func getFloatConverter(data interface{}) floatConverter { + switch data.(type) { + case (float32): + return func(i interface{}) float64 { + return float64(i.(float32)) + } + } + + return func(i interface{}) float64 { + return i.(float64) + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/distinct.go b/vendor/github.com/ahmetalpbalkan/go-linq/distinct.go new file mode 100644 index 0000000..7a32c31 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/distinct.go @@ -0,0 +1,75 @@ +package linq + +// Distinct method returns distinct elements from a collection. +// The result is an unordered collection that contains no duplicate values. +func (q Query) Distinct() Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + set := make(map[interface{}]bool) + + return func() (item interface{}, ok bool) { + for item, ok = next(); ok; item, ok = next() { + if _, has := set[item]; !has { + set[item] = true + return + } + } + + return + } + }, + } +} + +// Distinct method returns distinct elements from a collection. +// The result is an ordered collection that contains no duplicate values. +// +// NOTE: Distinct method on OrderedQuery type has better performance than +// Distinct method on Query type +func (oq OrderedQuery) Distinct() OrderedQuery { + return OrderedQuery{ + orders: oq.orders, + Query: Query{ + Iterate: func() Iterator { + next := oq.Iterate() + var prev interface{} + + return func() (item interface{}, ok bool) { + for item, ok = next(); ok; item, ok = next() { + if item != prev { + prev = item + return + } + } + + return + } + }, + }, + } +} + +// DistinctBy method returns distinct elements from a collection. This method +// executes selector function for each element to determine a value to compare. +// The result is an unordered collection that contains no duplicate values. +func (q Query) DistinctBy(selector func(interface{}) interface{}) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + set := make(map[interface{}]bool) + + return func() (item interface{}, ok bool) { + for item, ok = next(); ok; item, ok = next() { + s := selector(item) + if _, has := set[s]; !has { + set[s] = true + return + } + } + + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/doc.go b/vendor/github.com/ahmetalpbalkan/go-linq/doc.go new file mode 100644 index 0000000..8231f70 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/doc.go @@ -0,0 +1,5 @@ +// Package linq provides methods for querying and manipulating +// slices, arrays, maps, strings, channels and collections. +// +// Authors: Alexander Kalankhodzhaev (kalan), Ahmet Alp Balkan +package linq diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/except.go b/vendor/github.com/ahmetalpbalkan/go-linq/except.go new file mode 100644 index 0000000..c34a6b4 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/except.go @@ -0,0 +1,59 @@ +package linq + +// Except produces the set difference of two sequences. +// The set difference is the members of the first sequence +// that don't appear in the second sequence. +func (q Query) Except(q2 Query) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + + next2 := q2.Iterate() + set := make(map[interface{}]bool) + for i, ok := next2(); ok; i, ok = next2() { + set[i] = true + } + + return func() (item interface{}, ok bool) { + for item, ok = next(); ok; item, ok = next() { + if _, has := set[item]; !has { + return + } + } + + return + } + }, + } +} + +// ExceptBy invokes a transform function on each element of a collection +// and produces the set difference of two sequences. +// The set difference is the members of the first sequence +// that don't appear in the second sequence. +func (q Query) ExceptBy( + q2 Query, selector func(interface{}) interface{}) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + + next2 := q2.Iterate() + set := make(map[interface{}]bool) + for i, ok := next2(); ok; i, ok = next2() { + s := selector(i) + set[s] = true + } + + return func() (item interface{}, ok bool) { + for item, ok = next(); ok; item, ok = next() { + s := selector(item) + if _, has := set[s]; !has { + return + } + } + + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/from.go b/vendor/github.com/ahmetalpbalkan/go-linq/from.go new file mode 100644 index 0000000..56ab451 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/from.go @@ -0,0 +1,172 @@ +package linq + +import "reflect" + +// Iterator is an alias for function to iterate over data. +type Iterator func() (item interface{}, ok bool) + +// Query is the type returned from query functions. +// It can be iterated manually as shown in the example. +type Query struct { + Iterate func() Iterator +} + +// KeyValue is a type that is used to iterate over a map +// (if query is created from a map). This type is also used by +// ToMap() method to output result of a query into a map. +type KeyValue struct { + Key interface{} + Value interface{} +} + +// Iterable is an interface that has to be implemented by a +// custom collection in order to work with linq. +type Iterable interface { + Iterate() Iterator +} + +// From initializes a linq query with passed slice, array or map +// as the source. String, channel or struct implementing Iterable +// interface can be used as an input. In this case From delegates it +// to FromString, FromChannel and FromIterable internally. +func From(source interface{}) Query { + src := reflect.ValueOf(source) + + switch src.Kind() { + case reflect.Slice, reflect.Array: + len := src.Len() + + return Query{ + Iterate: func() Iterator { + index := 0 + + return func() (item interface{}, ok bool) { + ok = index < len + if ok { + item = src.Index(index).Interface() + index++ + } + + return + } + }, + } + case reflect.Map: + len := src.Len() + + return Query{ + Iterate: func() Iterator { + index := 0 + keys := src.MapKeys() + + return func() (item interface{}, ok bool) { + ok = index < len + if ok { + key := keys[index] + item = KeyValue{ + Key: key.Interface(), + Value: src.MapIndex(key).Interface(), + } + + index++ + } + + return + } + }, + } + case reflect.String: + return FromString(source.(string)) + case reflect.Chan: + return FromChannel(source.(chan interface{})) + default: + return FromIterable(source.(Iterable)) + } +} + +// FromChannel initializes a linq query with passed channel, +// linq iterates over channel until it is closed. +func FromChannel(source <-chan interface{}) Query { + return Query{ + Iterate: func() Iterator { + return func() (item interface{}, ok bool) { + item, ok = <-source + return + } + }, + } +} + +// FromString initializes a linq query with passed string, +// linq iterates over runes of string. +func FromString(source string) Query { + runes := []rune(source) + len := len(runes) + + return Query{ + Iterate: func() Iterator { + index := 0 + + return func() (item interface{}, ok bool) { + ok = index < len + if ok { + item = runes[index] + index++ + } + + return + } + }, + } +} + +// FromIterable initializes a linq query with custom collection passed. +// This collection has to implement Iterable interface, linq iterates over items, +// that has to implement Comparable interface or be basic types. +func FromIterable(source Iterable) Query { + return Query{ + Iterate: source.Iterate, + } +} + +// Range generates a sequence of integral numbers within a specified range. +func Range(start, count int) Query { + return Query{ + Iterate: func() Iterator { + index := 0 + current := start + + return func() (item interface{}, ok bool) { + if index >= count { + return nil, false + } + + item, ok = current, true + + index++ + current++ + return + } + }, + } +} + +// Repeat generates a sequence that contains one repeated value. +func Repeat(value interface{}, count int) Query { + return Query{ + Iterate: func() Iterator { + index := 0 + + return func() (item interface{}, ok bool) { + if index >= count { + return nil, false + } + + item, ok = value, true + + index++ + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/groupby.go b/vendor/github.com/ahmetalpbalkan/go-linq/groupby.go new file mode 100644 index 0000000..2affbf8 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/groupby.go @@ -0,0 +1,48 @@ +package linq + +// Group is a type that is used to store the result of GroupBy method. +type Group struct { + Key interface{} + Group []interface{} +} + +// GroupBy method groups the elements of a collection according +// to a specified key selector function and projects the elements for each group +// by using a specified function. +func (q Query) GroupBy( + keySelector func(interface{}) interface{}, + elementSelector func(interface{}) interface{}, +) Query { + + return Query{ + func() Iterator { + next := q.Iterate() + set := make(map[interface{}][]interface{}) + + for item, ok := next(); ok; item, ok = next() { + key := keySelector(item) + set[key] = append(set[key], elementSelector(item)) + } + + len := len(set) + idx := 0 + groups := make([]Group, len) + for k, v := range set { + groups[idx] = Group{k, v} + idx++ + } + + index := 0 + + return func() (item interface{}, ok bool) { + ok = index < len + if ok { + item = groups[index] + index++ + } + + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/groupjoin.go b/vendor/github.com/ahmetalpbalkan/go-linq/groupjoin.go new file mode 100644 index 0000000..7a3b56b --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/groupjoin.go @@ -0,0 +1,50 @@ +package linq + +// GroupJoin correlates the elements of two collections based on key equality, +// and groups the results. +// +// This method produces hierarchical results, which means that elements from outer query +// are paired with collections of matching elements from inner. GroupJoin enables you +// to base your results on a whole set of matches for each element of outer query. +// +// The resultSelector function is called only one time for each outer element +// together with a collection of all the inner elements that match the outer element. +// This differs from the Join method, in which the result selector function is invoked +// on pairs that contain one element from outer and one element from inner. +// +// GroupJoin preserves the order of the elements of outer, and for each element of outer, +// the order of the matching elements from inner. +func (q Query) GroupJoin( + inner Query, + outerKeySelector func(interface{}) interface{}, + innerKeySelector func(interface{}) interface{}, + resultSelector func(outer interface{}, inners []interface{}) interface{}, +) Query { + + return Query{ + Iterate: func() Iterator { + outernext := q.Iterate() + innernext := inner.Iterate() + + innerLookup := make(map[interface{}][]interface{}) + for innerItem, ok := innernext(); ok; innerItem, ok = innernext() { + innerKey := innerKeySelector(innerItem) + innerLookup[innerKey] = append(innerLookup[innerKey], innerItem) + } + + return func() (item interface{}, ok bool) { + if item, ok = outernext(); !ok { + return + } + + if group, has := innerLookup[outerKeySelector(item)]; !has { + item = resultSelector(item, []interface{}{}) + } else { + item = resultSelector(item, group) + } + + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/intersect.go b/vendor/github.com/ahmetalpbalkan/go-linq/intersect.go new file mode 100644 index 0000000..6499bb8 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/intersect.go @@ -0,0 +1,67 @@ +package linq + +// Intersect produces the set intersection of the source collection and the +// provided input collection. The intersection of two sets A and B is defined as +// the set that contains all the elements of A that also appear in B, +// but no other elements. +func (q Query) Intersect(q2 Query) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + next2 := q2.Iterate() + + set := make(map[interface{}]bool) + for item, ok := next2(); ok; item, ok = next2() { + set[item] = true + } + + return func() (item interface{}, ok bool) { + for item, ok = next(); ok; item, ok = next() { + if _, has := set[item]; has { + delete(set, item) + return + } + } + + return + } + }, + } +} + +// IntersectBy produces the set intersection of the source collection and the +// provided input collection. The intersection of two sets A and B is defined as +// the set that contains all the elements of A that also appear in B, +// but no other elements. +// +// IntersectBy invokes a transform function on each element of both collections. +func (q Query) IntersectBy( + q2 Query, + selector func(interface{}) interface{}, +) Query { + + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + next2 := q2.Iterate() + + set := make(map[interface{}]bool) + for item, ok := next2(); ok; item, ok = next2() { + s := selector(item) + set[s] = true + } + + return func() (item interface{}, ok bool) { + for item, ok = next(); ok; item, ok = next() { + s := selector(item) + if _, has := set[s]; has { + delete(set, s) + return + } + } + + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/join.go b/vendor/github.com/ahmetalpbalkan/go-linq/join.go new file mode 100644 index 0000000..95cb835 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/join.go @@ -0,0 +1,56 @@ +package linq + +// Join correlates the elements of two collection based on matching keys. +// +// A join refers to the operation of correlating the elements of two sources +// of information based on a common key. Join brings the two information sources +// and the keys by which they are matched together in one method call. +// This differs from the use of SelectMany, which requires more than one method call +// to perform the same operation. +// +// Join preserves the order of the elements of outer collection, +// and for each of these elements, the order of the matching elements of inner. +func (q Query) Join( + inner Query, + outerKeySelector func(interface{}) interface{}, + innerKeySelector func(interface{}) interface{}, + resultSelector func(outer interface{}, inner interface{}) interface{}, +) Query { + + return Query{ + Iterate: func() Iterator { + outernext := q.Iterate() + innernext := inner.Iterate() + + innerLookup := make(map[interface{}][]interface{}) + for innerItem, ok := innernext(); ok; innerItem, ok = innernext() { + innerKey := innerKeySelector(innerItem) + innerLookup[innerKey] = append(innerLookup[innerKey], innerItem) + } + + var outerItem interface{} + var innerGroup []interface{} + innerLen, innerIndex := 0, 0 + + return func() (item interface{}, ok bool) { + if innerIndex >= innerLen { + has := false + for !has { + outerItem, ok = outernext() + if !ok { + return + } + + innerGroup, has = innerLookup[outerKeySelector(outerItem)] + innerLen = len(innerGroup) + innerIndex = 0 + } + } + + item = resultSelector(outerItem, innerGroup[innerIndex]) + innerIndex++ + return item, true + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/orderby.go b/vendor/github.com/ahmetalpbalkan/go-linq/orderby.go new file mode 100644 index 0000000..ff7f5ce --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/orderby.go @@ -0,0 +1,212 @@ +package linq + +import "sort" + +type order struct { + selector func(interface{}) interface{} + compare comparer + desc bool +} + +// OrderedQuery is the type returned from OrderBy, OrderByDescending +// ThenBy and ThenByDescending functions. +type OrderedQuery struct { + Query + original Query + orders []order +} + +// OrderBy sorts the elements of a collection in ascending order. +// Elements are sorted according to a key. +func (q Query) OrderBy( + selector func(interface{}) interface{}) OrderedQuery { + return OrderedQuery{ + orders: []order{{selector: selector}}, + original: q, + Query: Query{ + Iterate: func() Iterator { + items := q.sort([]order{{selector: selector}}) + len := len(items) + index := 0 + + return func() (item interface{}, ok bool) { + ok = index < len + if ok { + item = items[index] + index++ + } + + return + } + }, + }, + } +} + +// OrderByDescending sorts the elements of a collection in descending order. +// Elements are sorted according to a key. +func (q Query) OrderByDescending( + selector func(interface{}) interface{}) OrderedQuery { + return OrderedQuery{ + orders: []order{{selector: selector, desc: true}}, + original: q, + Query: Query{ + Iterate: func() Iterator { + items := q.sort([]order{{selector: selector, desc: true}}) + len := len(items) + index := 0 + + return func() (item interface{}, ok bool) { + ok = index < len + if ok { + item = items[index] + index++ + } + + return + } + }, + }, + } +} + +// ThenBy performs a subsequent ordering of the elements in a collection +// in ascending order. This method enables you to specify multiple sort criteria +// by applying any number of ThenBy or ThenByDescending methods. +func (oq OrderedQuery) ThenBy( + selector func(interface{}) interface{}) OrderedQuery { + return OrderedQuery{ + orders: append(oq.orders, order{selector: selector}), + original: oq.original, + Query: Query{ + Iterate: func() Iterator { + items := oq.original.sort(append(oq.orders, order{selector: selector})) + len := len(items) + index := 0 + + return func() (item interface{}, ok bool) { + ok = index < len + if ok { + item = items[index] + index++ + } + + return + } + }, + }, + } +} + +// ThenByDescending performs a subsequent ordering of the elements in a collection +// in descending order. This method enables you to specify multiple sort criteria +// by applying any number of ThenBy or ThenByDescending methods. +func (oq OrderedQuery) ThenByDescending( + selector func(interface{}) interface{}) OrderedQuery { + return OrderedQuery{ + orders: append(oq.orders, order{selector: selector, desc: true}), + original: oq.original, + Query: Query{ + Iterate: func() Iterator { + items := oq.original.sort(append(oq.orders, order{selector: selector, desc: true})) + len := len(items) + index := 0 + + return func() (item interface{}, ok bool) { + ok = index < len + if ok { + item = items[index] + index++ + } + + return + } + }, + }, + } +} + +// Sort returns a new query by sorting elements with provided less function +// in ascending order. The comparer function should return true if the parameter i +// is less than j. While this method is uglier than chaining OrderBy, OrderByDescending, +// ThenBy and ThenByDescending methods, it's performance is much better. +func (q Query) Sort(less func(i, j interface{}) bool) Query { + return Query{ + Iterate: func() Iterator { + items := q.lessSort(less) + len := len(items) + index := 0 + + return func() (item interface{}, ok bool) { + ok = index < len + if ok { + item = items[index] + index++ + } + + return + } + }, + } +} + +type sorter struct { + items []interface{} + less func(i, j interface{}) bool +} + +func (s sorter) Len() int { + return len(s.items) +} + +func (s sorter) Swap(i, j int) { + s.items[i], s.items[j] = s.items[j], s.items[i] +} + +func (s sorter) Less(i, j int) bool { + return s.less(s.items[i], s.items[j]) +} + +func (q Query) sort(orders []order) (r []interface{}) { + next := q.Iterate() + for item, ok := next(); ok; item, ok = next() { + r = append(r, item) + } + + for i, j := range orders { + orders[i].compare = getComparer(j.selector(r[0])) + } + + s := sorter{ + items: r, + less: func(i, j interface{}) bool { + for _, order := range orders { + x, y := order.selector(i), order.selector(j) + switch order.compare(x, y) { + case 0: + continue + case -1: + return !order.desc + default: + return order.desc + } + } + + return false + }} + + sort.Sort(s) + return +} + +func (q Query) lessSort(less func(i, j interface{}) bool) (r []interface{}) { + next := q.Iterate() + for item, ok := next(); ok; item, ok = next() { + r = append(r, item) + } + + s := sorter{items: r, less: less} + + sort.Sort(s) + return +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/result.go b/vendor/github.com/ahmetalpbalkan/go-linq/result.go new file mode 100644 index 0000000..979be93 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/result.go @@ -0,0 +1,394 @@ +package linq + +import ( + "math" + "reflect" +) + +// All determines whether all elements of a collection satisfy a condition. +func (q Query) All(predicate func(interface{}) bool) bool { + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + if !predicate(item) { + return false + } + } + + return true +} + +// Any determines whether any element of a collection exists. +func (q Query) Any() bool { + _, ok := q.Iterate()() + return ok +} + +// AnyWith determines whether any element of a collection satisfies a condition. +func (q Query) AnyWith(predicate func(interface{}) bool) bool { + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + if predicate(item) { + return true + } + } + + return false +} + +// Average computes the average of a collection of numeric values. +func (q Query) Average() (r float64) { + next := q.Iterate() + item, ok := next() + if !ok { + return math.NaN() + } + + n := 1 + switch item.(type) { + case int, int8, int16, int32, int64: + conv := getIntConverter(item) + sum := conv(item) + + for item, ok = next(); ok; item, ok = next() { + sum += conv(item) + n++ + } + + r = float64(sum) + case uint, uint8, uint16, uint32, uint64: + conv := getUIntConverter(item) + sum := conv(item) + + for item, ok = next(); ok; item, ok = next() { + sum += conv(item) + n++ + } + + r = float64(sum) + default: + conv := getFloatConverter(item) + r = conv(item) + + for item, ok = next(); ok; item, ok = next() { + r += conv(item) + n++ + } + } + + return r / float64(n) +} + +// Contains determines whether a collection contains a specified element. +func (q Query) Contains(value interface{}) bool { + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + if item == value { + return true + } + } + + return false +} + +// Count returns the number of elements in a collection. +func (q Query) Count() (r int) { + next := q.Iterate() + + for _, ok := next(); ok; _, ok = next() { + r++ + } + + return +} + +// CountWith returns a number that represents how many elements +// in the specified collection satisfy a condition. +func (q Query) CountWith(predicate func(interface{}) bool) (r int) { + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + if predicate(item) { + r++ + } + } + + return +} + +// First returns the first element of a collection. +func (q Query) First() interface{} { + item, _ := q.Iterate()() + return item +} + +// FirstWith returns the first element of a collection that satisfies +// a specified condition. +func (q Query) FirstWith(predicate func(interface{}) bool) interface{} { + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + if predicate(item) { + return item + } + } + + return nil +} + +// Last returns the last element of a collection. +func (q Query) Last() (r interface{}) { + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + r = item + } + + return +} + +// LastWith returns the last element of a collection that satisfies +// a specified condition. +func (q Query) LastWith(predicate func(interface{}) bool) (r interface{}) { + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + if predicate(item) { + r = item + } + } + + return +} + +// Max returns the maximum value in a collection of values. +func (q Query) Max() (r interface{}) { + next := q.Iterate() + item, ok := next() + if !ok { + return nil + } + + compare := getComparer(item) + r = item + + for item, ok := next(); ok; item, ok = next() { + if compare(item, r) > 0 { + r = item + } + } + + return +} + +// Min returns the minimum value in a collection of values. +func (q Query) Min() (r interface{}) { + next := q.Iterate() + item, ok := next() + if !ok { + return nil + } + + compare := getComparer(item) + r = item + + for item, ok := next(); ok; item, ok = next() { + if compare(item, r) < 0 { + r = item + } + } + + return +} + +// Results iterates over a collection and returnes slice of interfaces +func (q Query) Results() (r []interface{}) { + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + r = append(r, item) + } + + return +} + +// SequenceEqual determines whether two collections are equal. +func (q Query) SequenceEqual(q2 Query) bool { + next := q.Iterate() + next2 := q2.Iterate() + + for item, ok := next(); ok; item, ok = next() { + item2, ok2 := next2() + if !ok2 || item != item2 { + return false + } + } + + _, ok2 := next2() + return !ok2 +} + +// Single returns the only element of a collection, and nil +// if there is not exactly one element in the collection. +func (q Query) Single() interface{} { + next := q.Iterate() + item, ok := next() + if !ok { + return nil + } + + _, ok = next() + if ok { + return nil + } + + return item +} + +// SingleWith returns the only element of a collection that satisfies +// a specified condition, and nil if more than one such element exists. +func (q Query) SingleWith(predicate func(interface{}) bool) (r interface{}) { + next := q.Iterate() + found := false + + for item, ok := next(); ok; item, ok = next() { + if predicate(item) { + if found { + return nil + } + + found = true + r = item + } + } + + return +} + +// SumInts computes the sum of a collection of numeric values. +// +// Values can be of any integer type: int, int8, int16, int32, int64. +// The result is int64. Method returns zero if collection contains no elements. +func (q Query) SumInts() (r int64) { + next := q.Iterate() + item, ok := next() + if !ok { + return 0 + } + + conv := getIntConverter(item) + r = conv(item) + + for item, ok = next(); ok; item, ok = next() { + r += conv(item) + } + + return +} + +// SumUInts computes the sum of a collection of numeric values. +// +// Values can be of any unsigned integer type: uint, uint8, uint16, uint32, uint64. +// The result is uint64. Method returns zero if collection contains no elements. +func (q Query) SumUInts() (r uint64) { + next := q.Iterate() + item, ok := next() + if !ok { + return 0 + } + + conv := getUIntConverter(item) + r = conv(item) + + for item, ok = next(); ok; item, ok = next() { + r += conv(item) + } + + return +} + +// SumFloats computes the sum of a collection of numeric values. +// +// Values can be of any float type: float32 or float64. The result is float64. +// Method returns zero if collection contains no elements. +func (q Query) SumFloats() (r float64) { + next := q.Iterate() + item, ok := next() + if !ok { + return 0 + } + + conv := getFloatConverter(item) + r = conv(item) + + for item, ok = next(); ok; item, ok = next() { + r += conv(item) + } + + return +} + +// ToChannel iterates over a collection and outputs each element +// to a channel, then closes it. +func (q Query) ToChannel(result chan<- interface{}) { + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + result <- item + } + + close(result) +} + +// ToMap iterates over a collection and populates result map with elements. +// Collection elements have to be of KeyValue type to use this method. +// To populate a map with elements of different type use ToMapBy method. +func (q Query) ToMap(result interface{}) { + q.ToMapBy( + result, + func(i interface{}) interface{} { + return i.(KeyValue).Key + }, + func(i interface{}) interface{} { + return i.(KeyValue).Value + }) +} + +// ToMapBy iterates over a collection and populates result map with elements. +// Functions keySelector and valueSelector are executed for each element of the collection +// to generate key and value for the map. Generated key and value types must be assignable +// to the map's key and value types. +func (q Query) ToMapBy( + result interface{}, + keySelector func(interface{}) interface{}, + valueSelector func(interface{}) interface{}, +) { + res := reflect.ValueOf(result) + m := reflect.Indirect(res) + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + key := reflect.ValueOf(keySelector(item)) + value := reflect.ValueOf(valueSelector(item)) + + m.SetMapIndex(key, value) + } + + res.Elem().Set(m) +} + +// ToSlice iterates over a collection and populates result slice with elements. +// Collection elements must be assignable to the slice's element type. +func (q Query) ToSlice(result interface{}) { + res := reflect.ValueOf(result) + slice := reflect.Indirect(res) + next := q.Iterate() + + for item, ok := next(); ok; item, ok = next() { + slice = reflect.Append(slice, reflect.ValueOf(item)) + } + + res.Elem().Set(slice) +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/reverse.go b/vendor/github.com/ahmetalpbalkan/go-linq/reverse.go new file mode 100644 index 0000000..eaacb9f --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/reverse.go @@ -0,0 +1,30 @@ +package linq + +// Reverse inverts the order of the elements in a collection. +// +// Unlike OrderBy, this sorting method does not consider the actual values themselves +// in determining the order. Rather, it just returns the elements in the reverse order +// from which they are produced by the underlying source. +func (q Query) Reverse() Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + + items := []interface{}{} + for item, ok := next(); ok; item, ok = next() { + items = append(items, item) + } + + index := len(items) - 1 + return func() (item interface{}, ok bool) { + if index < 0 { + return + } + + item, ok = items[index], true + index-- + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/select.go b/vendor/github.com/ahmetalpbalkan/go-linq/select.go new file mode 100644 index 0000000..e607229 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/select.go @@ -0,0 +1,71 @@ +package linq + +// Select projects each element of a collection into a new form. +// Returns a query with the result of invoking the transform function +// on each element of original source. +// +// This projection method requires the transform function, selector, +// to produce one value for each value in the source collection. +// If selector returns a value that is itself a collection, +// it is up to the consumer to traverse the subcollections manually. +// In such a situation, it might be better for your query to return a single +// coalesced collection of values. To achieve this, use the SelectMany method +// instead of Select. Although SelectMany works similarly to Select, +// it differs in that the transform function returns a collection +// that is then expanded by SelectMany before it is returned. +func (q Query) Select(selector func(interface{}) interface{}) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + + return func() (item interface{}, ok bool) { + var it interface{} + it, ok = next() + if ok { + item = selector(it) + } + + return + } + }, + } +} + +// SelectIndexed projects each element of a collection into a new form +// by incorporating the element's index. Returns a query with the result +// of invoking the transform function on each element of original source. +// +// The first argument to selector represents the zero-based index of that element +// in the source collection. This can be useful if the elements are in a known order +// and you want to do something with an element at a particular index, +// for example. It can also be useful if you want to retrieve the index of one +// or more elements. The second argument to selector represents the element to process. +// +// This projection method requires the transform function, selector, +// to produce one value for each value in the source collection. +// If selector returns a value that is itself a collection, +// it is up to the consumer to traverse the subcollections manually. +// In such a situation, it might be better for your query to return a single +// coalesced collection of values. To achieve this, use the SelectMany method +// instead of Select. Although SelectMany works similarly to Select, +// it differs in that the transform function returns a collection +// that is then expanded by SelectMany before it is returned. +func (q Query) SelectIndexed(selector func(int, interface{}) interface{}) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + index := 0 + + return func() (item interface{}, ok bool) { + var it interface{} + it, ok = next() + if ok { + item = selector(index, it) + index++ + } + + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/selectmany.go b/vendor/github.com/ahmetalpbalkan/go-linq/selectmany.go new file mode 100644 index 0000000..25ea8c3 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/selectmany.go @@ -0,0 +1,151 @@ +package linq + +// SelectMany projects each element of a collection to a Query, iterates and +// flattens the resulting collection into one collection. +func (q Query) SelectMany(selector func(interface{}) Query) Query { + return Query{ + Iterate: func() Iterator { + outernext := q.Iterate() + var inner interface{} + var innernext Iterator + + return func() (item interface{}, ok bool) { + for !ok { + if inner == nil { + inner, ok = outernext() + if !ok { + return + } + + innernext = selector(inner).Iterate() + } + + item, ok = innernext() + if !ok { + inner = nil + } + } + + return + } + }, + } +} + +// SelectManyIndexed projects each element of a collection to a Query, iterates and +// flattens the resulting collection into one collection. +// +// The first argument to selector represents the zero-based index of that element +// in the source collection. This can be useful if the elements are in a known order +// and you want to do something with an element at a particular index, for example. +// It can also be useful if you want to retrieve the index of one or more elements. +// The second argument to selector represents the element to process. +func (q Query) SelectManyIndexed(selector func(int, interface{}) Query) Query { + return Query{ + Iterate: func() Iterator { + outernext := q.Iterate() + index := 0 + var inner interface{} + var innernext Iterator + + return func() (item interface{}, ok bool) { + for !ok { + if inner == nil { + inner, ok = outernext() + if !ok { + return + } + + innernext = selector(index, inner).Iterate() + index++ + } + + item, ok = innernext() + if !ok { + inner = nil + } + } + + return + } + }, + } +} + +// SelectManyBy projects each element of a collection to a Query, iterates and +// flattens the resulting collection into one collection, and invokes +// a result selector function on each element therein. +func (q Query) SelectManyBy( + selector func(interface{}) Query, + resultSelector func(interface{}, interface{}) interface{}, +) Query { + + return Query{ + Iterate: func() Iterator { + outernext := q.Iterate() + var outer interface{} + var innernext Iterator + + return func() (item interface{}, ok bool) { + for !ok { + if outer == nil { + outer, ok = outernext() + if !ok { + return + } + + innernext = selector(outer).Iterate() + } + + item, ok = innernext() + if !ok { + outer = nil + } + } + + item = resultSelector(outer, item) + return + } + }, + } +} + +// SelectManyByIndexed projects each element of a collection to a Query, iterates and +// flattens the resulting collection into one collection, and invokes +// a result selector function on each element therein. +// The index of each source element is used in the intermediate projected form +// of that element. +func (q Query) SelectManyByIndexed(selector func(int, interface{}) Query, + resultSelector func(interface{}, interface{}) interface{}) Query { + + return Query{ + Iterate: func() Iterator { + outernext := q.Iterate() + index := 0 + var outer interface{} + var innernext Iterator + + return func() (item interface{}, ok bool) { + for !ok { + if outer == nil { + outer, ok = outernext() + if !ok { + return + } + + innernext = selector(index, outer).Iterate() + index++ + } + + item, ok = innernext() + if !ok { + outer = nil + } + } + + item = resultSelector(outer, item) + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/skip.go b/vendor/github.com/ahmetalpbalkan/go-linq/skip.go new file mode 100644 index 0000000..1110ae5 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/skip.go @@ -0,0 +1,91 @@ +package linq + +// Skip bypasses a specified number of elements in a collection +// and then returns the remaining elements. +func (q Query) Skip(count int) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + n := count + + return func() (item interface{}, ok bool) { + for ; n > 0; n-- { + item, ok = next() + if !ok { + return + } + } + + return next() + } + }, + } +} + +// SkipWhile bypasses elements in a collection as long as a specified condition is true +// and then returns the remaining elements. +// +// This method tests each element by using predicate and skips the element +// if the result is true. After the predicate function returns false for an element, +// that element and the remaining elements in source are returned +// and there are no more invocations of predicate. +func (q Query) SkipWhile(predicate func(interface{}) bool) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + ready := false + + return func() (item interface{}, ok bool) { + for !ready { + item, ok = next() + if !ok { + return + } + + ready = !predicate(item) + if ready { + return + } + } + + return next() + } + }, + } +} + +// SkipWhileIndexed bypasses elements in a collection as long as a specified condition +// is true and then returns the remaining elements. The element's index is used +// in the logic of the predicate function. +// +// This method tests each element by using predicate and skips the element +// if the result is true. After the predicate function returns false for an element, +// that element and the remaining elements in source are returned +// and there are no more invocations of predicate. +func (q Query) SkipWhileIndexed(predicate func(int, interface{}) bool) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + ready := false + index := 0 + + return func() (item interface{}, ok bool) { + for !ready { + item, ok = next() + if !ok { + return + } + + ready = !predicate(index, item) + if ready { + return + } + + index++ + } + + return next() + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/take.go b/vendor/github.com/ahmetalpbalkan/go-linq/take.go new file mode 100644 index 0000000..d21fc96 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/take.go @@ -0,0 +1,84 @@ +package linq + +// Take returns a specified number of contiguous elements from the start of a collection. +func (q Query) Take(count int) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + n := count + + return func() (item interface{}, ok bool) { + if n <= 0 { + return + } + + n-- + return next() + } + }, + } +} + +// TakeWhile returns elements from a collection as long as a specified condition is true, +// and then skips the remaining elements. +func (q Query) TakeWhile(predicate func(interface{}) bool) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + done := false + + return func() (item interface{}, ok bool) { + if done { + return + } + + item, ok = next() + if !ok { + done = true + return + } + + if predicate(item) { + return + } + + done = true + return nil, false + } + }, + } +} + +// TakeWhileIndexed returns elements from a collection as long as a specified condition +// is true. The element's index is used in the logic of the predicate function. +// The first argument of predicate represents the zero-based index of the element +// within collection. The second argument represents the element to test. +func (q Query) TakeWhileIndexed(predicate func(int, interface{}) bool) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + done := false + index := 0 + + return func() (item interface{}, ok bool) { + if done { + return + } + + item, ok = next() + if !ok { + done = true + return + } + + if predicate(index, item) { + index++ + return + } + + done = true + return nil, false + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/union.go b/vendor/github.com/ahmetalpbalkan/go-linq/union.go new file mode 100644 index 0000000..9ca4b65 --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/union.go @@ -0,0 +1,41 @@ +package linq + +// Union produces the set union of two collections. +// +// This method excludes duplicates from the return set. +// This is different behavior to the Concat method, +// which returns all the elements in the input collection +// including duplicates. +func (q Query) Union(q2 Query) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + next2 := q2.Iterate() + + set := make(map[interface{}]bool) + use1 := true + + return func() (item interface{}, ok bool) { + if use1 { + for item, ok = next(); ok; item, ok = next() { + if _, has := set[item]; !has { + set[item] = true + return + } + } + + use1 = false + } + + for item, ok = next2(); ok; item, ok = next2() { + if _, has := set[item]; !has { + set[item] = true + return + } + } + + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/where.go b/vendor/github.com/ahmetalpbalkan/go-linq/where.go new file mode 100644 index 0000000..26d0bda --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/where.go @@ -0,0 +1,46 @@ +package linq + +// Where filters a collection of values based on a predicate. +func (q Query) Where(predicate func(interface{}) bool) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + + return func() (item interface{}, ok bool) { + for item, ok = next(); ok; item, ok = next() { + if predicate(item) { + return + } + } + + return + } + }, + } +} + +// WhereIndexed filters a collection of values based on a predicate. +// Each element's index is used in the logic of the predicate function. +// +// The first argument represents the zero-based index of the element within collection. +// The second argument of predicate represents the element to test. +func (q Query) WhereIndexed(predicate func(int, interface{}) bool) Query { + return Query{ + Iterate: func() Iterator { + next := q.Iterate() + index := 0 + + return func() (item interface{}, ok bool) { + for item, ok = next(); ok; item, ok = next() { + if predicate(index, item) { + return + } + + index++ + } + + return + } + }, + } +} diff --git a/vendor/github.com/ahmetalpbalkan/go-linq/zip.go b/vendor/github.com/ahmetalpbalkan/go-linq/zip.go new file mode 100644 index 0000000..1314b9a --- /dev/null +++ b/vendor/github.com/ahmetalpbalkan/go-linq/zip.go @@ -0,0 +1,35 @@ +package linq + +// Zip applies a specified function to the corresponding elements +// of two collections, producing a collection of the results. +// +// The method steps through the two input collections, applying function +// resultSelector to corresponding elements of the two collections. +// The method returns a collection of the values that are returned by resultSelector. +// If the input collections do not have the same number of elements, +// the method combines elements until it reaches the end of one of the collections. +// For example, if one collection has three elements and the other one has four, +// the result collection has only three elements. +func (q Query) Zip( + q2 Query, + resultSelector func(interface{}, interface{}) interface{}, +) Query { + + return Query{ + Iterate: func() Iterator { + next1 := q.Iterate() + next2 := q2.Iterate() + + return func() (item interface{}, ok bool) { + item1, ok1 := next1() + item2, ok2 := next2() + + if ok1 && ok2 { + return resultSelector(item1, item2), true + } + + return nil, false + } + }, + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 522fc3e..7aca8b1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,2 +1,4 @@ +# github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible +github.com/ahmetalpbalkan/go-linq # github.com/go-ini/ini v1.57.0 github.com/go-ini/ini