├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── aggregate.go ├── aggregate_test.go ├── benchmark_test.go ├── compare.go ├── compare_test.go ├── concat.go ├── concat_test.go ├── convert.go ├── convert_test.go ├── defaultifempty.go ├── defaultifempty_test.go ├── distinct.go ├── distinct_test.go ├── doc.go ├── example_test.go ├── except.go ├── except_test.go ├── from.go ├── from_test.go ├── general_test.go ├── genericfunc.go ├── genericfunc_test.go ├── go.mod ├── groupby.go ├── groupby_test.go ├── groupjoin.go ├── groupjoin_test.go ├── index.go ├── index_test.go ├── intersect.go ├── intersect_test.go ├── join.go ├── join_test.go ├── orderby.go ├── orderby_test.go ├── result.go ├── result_test.go ├── reverse.go ├── reverse_test.go ├── select.go ├── select_test.go ├── selectmany.go ├── selectmany_test.go ├── setup_test.go ├── skip.go ├── skip_test.go ├── take.go ├── take_test.go ├── union.go ├── union_test.go ├── where.go ├── where_test.go ├── zip.go └── zip_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | ### Code ### 27 | # Visual Studio Code - https://code.visualstudio.com/ 28 | .settings/ 29 | .vscode/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | language: go 4 | go: 5 | - 1.11 6 | - tip 7 | 8 | install: true 9 | env: 10 | - GO111MODULE=on 11 | 12 | script: 13 | - go vet -x ./... 14 | - test -z "$(golint ./...)" 15 | - test -z "$(gofmt -s -l -w . | tee /dev/stderr)" 16 | - go test -v ./... 17 | - go test -covermode=count -coverprofile=profile.cov 18 | - test -z "$(apicompat -before ${TRAVIS_COMMIT_RANGE%...*} -after ${TRAVIS_COMMIT_RANGE#*...} ./... | tee /dev/stderr)" 19 | 20 | after_script: 21 | - goveralls -coverprofile=profile.cov -service=travis-ci 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2016 Ahmet Alp Balkan 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-linq [![GoDoc](https://godoc.org/github.com/ahmetb/go-linq?status.svg)](https://godoc.org/github.com/ahmetb/go-linq) [![Build Status](https://travis-ci.org/ahmetb/go-linq.svg?branch=master)](https://travis-ci.org/ahmetb/go-linq) [![Coverage Status](https://coveralls.io/repos/github/ahmetb/go-linq/badge.svg?branch=master)](https://coveralls.io/github/ahmetb/go-linq?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ahmetb/go-linq)](https://goreportcard.com/report/github.com/ahmetb/go-linq) 2 | 3 | A powerful language integrated query (LINQ) library for Go. 4 | 5 | * Written in vanilla Go, no dependencies! 6 | * Complete lazy evaluation with iterator pattern 7 | * Safe for concurrent use 8 | * Supports generic functions to make your code cleaner and free of type assertions 9 | * Supports arrays, slices, maps, strings, channels and custom collections 10 | 11 | ## Installation 12 | 13 | When used with Go modules, use the following import path: 14 | 15 | go get github.com/ahmetb/go-linq/v3 16 | 17 | Older versions of Go using different dependency management tools can use the 18 | following import path to prevent breaking API changes: 19 | 20 | go get gopkg.in/ahmetb/go-linq.v3 21 | 22 | ## Quickstart 23 | 24 | Usage is as easy as chaining methods like: 25 | 26 | `From(slice)` `.Where(predicate)` `.Select(selector)` `.Union(data)` 27 | 28 | **Example 1: Find all owners of cars manufactured after 2015** 29 | 30 | ```go 31 | import . "github.com/ahmetb/go-linq/v3" 32 | 33 | type Car struct { 34 | year int 35 | owner, model string 36 | } 37 | 38 | ... 39 | 40 | 41 | var owners []string 42 | 43 | From(cars).Where(func(c interface{}) bool { 44 | return c.(Car).year >= 2015 45 | }).Select(func(c interface{}) interface{} { 46 | return c.(Car).owner 47 | }).ToSlice(&owners) 48 | ``` 49 | 50 | Or, you can use generic functions, like `WhereT` and `SelectT` to simplify your code 51 | (at a performance penalty): 52 | 53 | ```go 54 | var owners []string 55 | 56 | From(cars).WhereT(func(c Car) bool { 57 | return c.year >= 2015 58 | }).SelectT(func(c Car) string { 59 | return c.owner 60 | }).ToSlice(&owners) 61 | ``` 62 | 63 | **Example 2: Find the author who has written the most books** 64 | 65 | ```go 66 | import . "github.com/ahmetb/go-linq/v3" 67 | 68 | type Book struct { 69 | id int 70 | title string 71 | authors []string 72 | } 73 | 74 | author := From(books).SelectMany( // make a flat array of authors 75 | func(book interface{}) Query { 76 | return From(book.(Book).authors) 77 | }).GroupBy( // group by author 78 | func(author interface{}) interface{} { 79 | return author // author as key 80 | }, func(author interface{}) interface{} { 81 | return author // author as value 82 | }).OrderByDescending( // sort groups by its length 83 | func(group interface{}) interface{} { 84 | return len(group.(Group).Group) 85 | }).Select( // get authors out of groups 86 | func(group interface{}) interface{} { 87 | return group.(Group).Key 88 | }).First() // take the first author 89 | ``` 90 | 91 | **Example 3: Implement a custom method that leaves only values greater than the specified threshold** 92 | 93 | ```go 94 | type MyQuery Query 95 | 96 | func (q MyQuery) GreaterThan(threshold int) Query { 97 | return Query{ 98 | Iterate: func() Iterator { 99 | next := q.Iterate() 100 | 101 | return func() (item interface{}, ok bool) { 102 | for item, ok = next(); ok; item, ok = next() { 103 | if item.(int) > threshold { 104 | return 105 | } 106 | } 107 | 108 | return 109 | } 110 | }, 111 | } 112 | } 113 | 114 | result := MyQuery(Range(1,10)).GreaterThan(5).Results() 115 | ``` 116 | 117 | ## Generic Functions 118 | 119 | Although Go doesn't implement generics, with some reflection tricks, you can use go-linq without 120 | typing `interface{}`s and type assertions. This will introduce a performance penalty (5x-10x slower) 121 | but will yield in a cleaner and more readable code. 122 | 123 | Methods with `T` suffix (such as `WhereT`) accept functions with generic types. So instead of 124 | 125 | .Select(func(v interface{}) interface{} {...}) 126 | 127 | you can type: 128 | 129 | .SelectT(func(v YourType) YourOtherType {...}) 130 | 131 | This will make your code free of `interface{}` and type assertions. 132 | 133 | **Example 4: "MapReduce" in a slice of string sentences to list the top 5 most used words using generic functions** 134 | 135 | ```go 136 | var results []string 137 | 138 | From(sentences). 139 | // split sentences to words 140 | SelectManyT(func(sentence string) Query { 141 | return From(strings.Split(sentence, " ")) 142 | }). 143 | // group the words 144 | GroupByT( 145 | func(word string) string { return word }, 146 | func(word string) string { return word }, 147 | ). 148 | // order by count 149 | OrderByDescendingT(func(wordGroup Group) int { 150 | return len(wordGroup.Group) 151 | }). 152 | // order by the word 153 | ThenByT(func(wordGroup Group) string { 154 | return wordGroup.Key.(string) 155 | }). 156 | Take(5). // take the top 5 157 | // project the words using the index as rank 158 | SelectIndexedT(func(index int, wordGroup Group) string { 159 | return fmt.Sprintf("Rank: #%d, Word: %s, Counts: %d", index+1, wordGroup.Key, len(wordGroup.Group)) 160 | }). 161 | ToSlice(&results) 162 | ``` 163 | 164 | **More examples** can be found in the [documentation](https://godoc.org/github.com/ahmetb/go-linq). 165 | 166 | ## Release Notes 167 | 168 | ```text 169 | v3.2.0 (2020-12-29) 170 | * Added FromChannelT(). 171 | * Added DefaultIfEmpty(). 172 | 173 | v3.1.0 (2019-07-09) 174 | * Support for Go modules 175 | * Added IndexOf()/IndexOfT(). 176 | 177 | v3.0.0 (2017-01-10) 178 | * Breaking change: ToSlice() now overwrites existing slice starting 179 | from index 0 and grows/reslices it as needed. 180 | * Generic methods support (thanks @cleitonmarx!) 181 | - Accepting parametrized functions was originally proposed in #26 182 | - You can now avoid type assertions and interface{}s 183 | - Functions with generic methods are named as "MethodNameT" and 184 | signature for the existing LINQ methods are unchanged. 185 | * Added ForEach(), ForEachIndexed() and AggregateWithSeedBy(). 186 | 187 | v2.0.0 (2016-09-02) 188 | * IMPORTANT: This release is a BREAKING CHANGE. The old version 189 | is archived at the 'archive/0.9' branch or the 0.9 tags. 190 | * A COMPLETE REWRITE of go-linq with better performance and memory 191 | efficiency. (thanks @kalaninja!) 192 | * API has significantly changed. Most notably: 193 | - linq.T removed in favor of interface{} 194 | - library methods no longer return errors 195 | - PLINQ removed for now (see channels support) 196 | - support for channels, custom collections and comparables 197 | 198 | v0.9-rc4 199 | * GroupBy() 200 | 201 | v0.9-rc3.2 202 | * bugfix: All() iterating over values instead of indices 203 | 204 | v0.9-rc3.1 205 | * bugfix: modifying result slice affects subsequent query methods 206 | 207 | v0.9-rc3 208 | * removed FirstOrNil, LastOrNil, ElementAtOrNil methods 209 | 210 | v0.9-rc2.5 211 | * slice-accepting methods accept slices of any type with reflections 212 | 213 | v0.9-rc2 214 | * parallel linq (plinq) implemented 215 | * Queryable separated into Query & ParallelQuery 216 | * fixed early termination for All 217 | 218 | v0.9-rc1 219 | * many linq methods are implemented 220 | * methods have error handling support 221 | * type assertion limitations are unresolved 222 | * travis-ci.org build integrated 223 | * open sourced on github, master & dev branches 224 | ``` 225 | -------------------------------------------------------------------------------- /aggregate.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Aggregate applies an accumulator function over a sequence. 4 | // 5 | // Aggregate method makes it simple to perform a calculation over a sequence of 6 | // values. This method works by calling f() one time for each element in source 7 | // except the first one. Each time f() is called, Aggregate passes both the 8 | // element from the sequence and an aggregated value (as the first argument to 9 | // f()). The first element of source is used as the initial aggregate value. The 10 | // result of f() replaces the previous aggregated value. 11 | // 12 | // Aggregate returns the final result of f(). 13 | func (q Query) Aggregate(f func(interface{}, interface{}) interface{}) interface{} { 14 | next := q.Iterate() 15 | 16 | result, any := next() 17 | if !any { 18 | return nil 19 | } 20 | 21 | for current, ok := next(); ok; current, ok = next() { 22 | result = f(result, current) 23 | } 24 | 25 | return result 26 | } 27 | 28 | // AggregateT is the typed version of Aggregate. 29 | // 30 | // - f is of type: func(TSource, TSource) TSource 31 | // 32 | // NOTE: Aggregate has better performance than AggregateT. 33 | func (q Query) AggregateT(f interface{}) interface{} { 34 | fGenericFunc, err := newGenericFunc( 35 | "AggregateT", "f", f, 36 | simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), 37 | ) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | fFunc := func(result interface{}, current interface{}) interface{} { 43 | return fGenericFunc.Call(result, current) 44 | } 45 | 46 | return q.Aggregate(fFunc) 47 | } 48 | 49 | // AggregateWithSeed applies an accumulator function over a sequence. The 50 | // specified seed value is used as the initial accumulator value. 51 | // 52 | // Aggregate method makes it simple to perform a calculation over a sequence of 53 | // values. This method works by calling f() one time for each element in source 54 | // except the first one. Each time f() is called, Aggregate passes both the 55 | // element from the sequence and an aggregated value (as the first argument to 56 | // f()). The value of the seed parameter is used as the initial aggregate value. 57 | // The result of f() replaces the previous aggregated value. 58 | // 59 | // Aggregate returns the final result of f(). 60 | func (q Query) AggregateWithSeed(seed interface{}, 61 | f func(interface{}, interface{}) interface{}) interface{} { 62 | 63 | next := q.Iterate() 64 | result := seed 65 | 66 | for current, ok := next(); ok; current, ok = next() { 67 | result = f(result, current) 68 | } 69 | 70 | return result 71 | } 72 | 73 | // AggregateWithSeedT is the typed version of AggregateWithSeed. 74 | // 75 | // - f is of type "func(TAccumulate, TSource) TAccumulate" 76 | // 77 | // NOTE: AggregateWithSeed has better performance than 78 | // AggregateWithSeedT. 79 | func (q Query) AggregateWithSeedT(seed interface{}, 80 | f interface{}) interface{} { 81 | fGenericFunc, err := newGenericFunc( 82 | "AggregateWithSeed", "f", f, 83 | simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), 84 | ) 85 | if err != nil { 86 | panic(err) 87 | } 88 | 89 | fFunc := func(result interface{}, current interface{}) interface{} { 90 | return fGenericFunc.Call(result, current) 91 | } 92 | 93 | return q.AggregateWithSeed(seed, fFunc) 94 | } 95 | 96 | // AggregateWithSeedBy applies an accumulator function over a sequence. The 97 | // specified seed value is used as the initial accumulator value, and the 98 | // specified function is used to select the result value. 99 | // 100 | // Aggregate method makes it simple to perform a calculation over a sequence of 101 | // values. This method works by calling f() one time for each element in source. 102 | // Each time func is called, Aggregate passes both the element from the sequence 103 | // and an aggregated value (as the first argument to func). The value of the 104 | // seed parameter is used as the initial aggregate value. The result of func 105 | // replaces the previous aggregated value. 106 | // 107 | // The final result of func is passed to resultSelector to obtain the final 108 | // result of Aggregate. 109 | func (q Query) AggregateWithSeedBy(seed interface{}, 110 | f func(interface{}, interface{}) interface{}, 111 | resultSelector func(interface{}) interface{}) interface{} { 112 | 113 | next := q.Iterate() 114 | result := seed 115 | 116 | for current, ok := next(); ok; current, ok = next() { 117 | result = f(result, current) 118 | } 119 | 120 | return resultSelector(result) 121 | } 122 | 123 | // AggregateWithSeedByT is the typed version of AggregateWithSeedBy. 124 | // 125 | // - f is of type "func(TAccumulate, TSource) TAccumulate" 126 | // - resultSelectorFn is of type "func(TAccumulate) TResult" 127 | // 128 | // NOTE: AggregateWithSeedBy has better performance than 129 | // AggregateWithSeedByT. 130 | func (q Query) AggregateWithSeedByT(seed interface{}, 131 | f interface{}, 132 | resultSelectorFn interface{}) interface{} { 133 | fGenericFunc, err := newGenericFunc( 134 | "AggregateWithSeedByT", "f", f, 135 | simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), 136 | ) 137 | if err != nil { 138 | panic(err) 139 | } 140 | 141 | fFunc := func(result interface{}, current interface{}) interface{} { 142 | return fGenericFunc.Call(result, current) 143 | } 144 | 145 | resultSelectorGenericFunc, err := newGenericFunc( 146 | "AggregateWithSeedByT", "resultSelectorFn", resultSelectorFn, 147 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 148 | ) 149 | if err != nil { 150 | panic(err) 151 | } 152 | 153 | resultSelectorFunc := func(result interface{}) interface{} { 154 | return resultSelectorGenericFunc.Call(result) 155 | } 156 | 157 | return q.AggregateWithSeedBy(seed, fFunc, resultSelectorFunc) 158 | } 159 | -------------------------------------------------------------------------------- /aggregate_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | import "strings" 5 | 6 | func TestAggregate(t *testing.T) { 7 | tests := []struct { 8 | input interface{} 9 | want interface{} 10 | }{ 11 | {[]string{"apple", "mango", "orange", "passionfruit", "grape"}, "passionfruit"}, 12 | {[]string{}, nil}, 13 | } 14 | 15 | for _, test := range tests { 16 | r := From(test.input).Aggregate(func(r interface{}, i interface{}) interface{} { 17 | if len(r.(string)) > len(i.(string)) { 18 | return r 19 | } 20 | return i 21 | }) 22 | 23 | if r != test.want { 24 | t.Errorf("From(%v).Aggregate()=%v expected %v", test.input, r, test.want) 25 | } 26 | } 27 | } 28 | 29 | func TestAggregateT_PanicWhenFunctionIsInvalid(t *testing.T) { 30 | mustPanicWithError(t, "AggregateT: parameter [f] has a invalid function signature. Expected: 'func(T,T)T', actual: 'func(int,string,string)string'", func() { 31 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).AggregateT(func(x int, r string, i string) string { 32 | if len(r) > len(i) { 33 | return r 34 | } 35 | return i 36 | }) 37 | }) 38 | } 39 | 40 | func TestAggregateWithSeed(t *testing.T) { 41 | input := []string{"apple", "mango", "orange", "banana", "grape"} 42 | want := "passionfruit" 43 | 44 | r := From(input).AggregateWithSeed(want, 45 | func(r interface{}, i interface{}) interface{} { 46 | if len(r.(string)) > len(i.(string)) { 47 | return r 48 | } 49 | return i 50 | }) 51 | 52 | if r != want { 53 | t.Errorf("From(%v).AggregateWithSeed()=%v expected %v", input, r, want) 54 | } 55 | } 56 | 57 | func TestAggregateWithSeedT_PanicWhenFunctionIsInvalid(t *testing.T) { 58 | mustPanicWithError(t, "AggregateWithSeed: parameter [f] has a invalid function signature. Expected: 'func(T,T)T', actual: 'func(int,string,string)string'", func() { 59 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).AggregateWithSeedT(3, func(x int, r string, i string) string { 60 | if len(r) > len(i) { 61 | return r 62 | } 63 | return i 64 | }) 65 | }) 66 | } 67 | 68 | func TestAggregateWithSeedBy(t *testing.T) { 69 | input := []string{"apple", "mango", "orange", "passionfruit", "grape"} 70 | want := "PASSIONFRUIT" 71 | 72 | r := From(input).AggregateWithSeedBy("banana", 73 | func(r interface{}, i interface{}) interface{} { 74 | if len(r.(string)) > len(i.(string)) { 75 | return r 76 | } 77 | return i 78 | }, 79 | func(r interface{}) interface{} { 80 | return strings.ToUpper(r.(string)) 81 | }, 82 | ) 83 | 84 | if r != want { 85 | t.Errorf("From(%v).AggregateWithSeed()=%v expected %v", input, r, want) 86 | } 87 | } 88 | 89 | func TestAggregateWithSeedByT_PanicWhenFunctionIsInvalid(t *testing.T) { 90 | mustPanicWithError(t, "AggregateWithSeedByT: parameter [f] has a invalid function signature. Expected: 'func(T,T)T', actual: 'func(int,string,string)string'", func() { 91 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).AggregateWithSeedByT(3, 92 | func(x int, r string, i string) string { 93 | if len(r) > len(i) { 94 | return r 95 | } 96 | return i 97 | }, 98 | func(r string) string { 99 | return r 100 | }, 101 | ) 102 | }) 103 | } 104 | 105 | func TestAggregateWithSeedByT_PanicWhenResultSelectorFnIsInvalid(t *testing.T) { 106 | mustPanicWithError(t, "AggregateWithSeedByT: parameter [resultSelectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(string,int)string'", func() { 107 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).AggregateWithSeedByT(3, 108 | func(x int, r int) int { 109 | if x > r { 110 | return x 111 | } 112 | return r 113 | }, 114 | func(r string, t int) string { 115 | return r 116 | }, 117 | ) 118 | }) 119 | } 120 | -------------------------------------------------------------------------------- /benchmark_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | const ( 6 | size = 1000000 7 | ) 8 | 9 | func BenchmarkSelectWhereFirst(b *testing.B) { 10 | for n := 0; n < b.N; n++ { 11 | Range(1, size).Select(func(i interface{}) interface{} { 12 | return -i.(int) 13 | }).Where(func(i interface{}) bool { 14 | return i.(int) > -1000 15 | }).First() 16 | } 17 | } 18 | 19 | func BenchmarkSelectWhereFirst_generics(b *testing.B) { 20 | for n := 0; n < b.N; n++ { 21 | Range(1, size).SelectT(func(i int) int { 22 | return -i 23 | }).WhereT(func(i int) bool { 24 | return i > -1000 25 | }).First() 26 | } 27 | } 28 | 29 | func BenchmarkSum(b *testing.B) { 30 | for n := 0; n < b.N; n++ { 31 | Range(1, size).Where(func(i interface{}) bool { 32 | return i.(int)%2 == 0 33 | }).SumInts() 34 | } 35 | } 36 | 37 | func BenchmarkSum_generics(b *testing.B) { 38 | for n := 0; n < b.N; n++ { 39 | Range(1, size).WhereT(func(i int) bool { 40 | return i%2 == 0 41 | }).SumInts() 42 | } 43 | } 44 | 45 | func BenchmarkZipSkipTake(b *testing.B) { 46 | for n := 0; n < b.N; n++ { 47 | Range(1, size).Zip(Range(1, size).Select(func(i interface{}) interface{} { 48 | return i.(int) * 2 49 | }), func(i, j interface{}) interface{} { 50 | return i.(int) + j.(int) 51 | }).Skip(2).Take(5) 52 | } 53 | } 54 | 55 | func BenchmarkZipSkipTake_generics(b *testing.B) { 56 | for n := 0; n < b.N; n++ { 57 | Range(1, size).ZipT(Range(1, size).SelectT(func(i int) int { 58 | return i * 2 59 | }), func(i, j int) int { 60 | return i + j 61 | }).Skip(2).Take(5) 62 | } 63 | } 64 | 65 | func BenchmarkFromChannel(b *testing.B) { 66 | for n := 0; n < b.N; n++ { 67 | ch := make(chan interface{}) 68 | go func() { 69 | for i := 0; i < size; i++ { 70 | ch <- i 71 | } 72 | 73 | close(ch) 74 | }() 75 | 76 | FromChannel(ch).All(func(i interface{}) bool { return true }) 77 | } 78 | } 79 | 80 | func BenchmarkFromChannelT(b *testing.B) { 81 | for n := 0; n < b.N; n++ { 82 | ch := make(chan interface{}) 83 | go func() { 84 | for i := 0; i < size; i++ { 85 | ch <- i 86 | } 87 | 88 | close(ch) 89 | }() 90 | 91 | FromChannelT(ch).All(func(i interface{}) bool { return true }) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /compare.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | type comparer func(interface{}, interface{}) int 4 | 5 | // Comparable is an interface that has to be implemented by a custom collection 6 | // elements in order to work with linq. 7 | // 8 | // Example: 9 | // func (f foo) CompareTo(c Comparable) int { 10 | // a, b := f.f1, c.(foo).f1 11 | // 12 | // if a < b { 13 | // return -1 14 | // } else if a > b { 15 | // return 1 16 | // } 17 | // 18 | // return 0 19 | // } 20 | type Comparable interface { 21 | CompareTo(Comparable) int 22 | } 23 | 24 | func getComparer(data interface{}) comparer { 25 | switch data.(type) { 26 | case int: 27 | return func(x, y interface{}) int { 28 | a, b := x.(int), y.(int) 29 | switch { 30 | case a > b: 31 | return 1 32 | case b > a: 33 | return -1 34 | default: 35 | return 0 36 | } 37 | } 38 | case int8: 39 | return func(x, y interface{}) int { 40 | a, b := x.(int8), y.(int8) 41 | switch { 42 | case a > b: 43 | return 1 44 | case b > a: 45 | return -1 46 | default: 47 | return 0 48 | } 49 | } 50 | case int16: 51 | return func(x, y interface{}) int { 52 | a, b := x.(int16), y.(int16) 53 | switch { 54 | case a > b: 55 | return 1 56 | case b > a: 57 | return -1 58 | default: 59 | return 0 60 | } 61 | } 62 | case int32: 63 | return func(x, y interface{}) int { 64 | a, b := x.(int32), y.(int32) 65 | switch { 66 | case a > b: 67 | return 1 68 | case b > a: 69 | return -1 70 | default: 71 | return 0 72 | } 73 | } 74 | case int64: 75 | return func(x, y interface{}) int { 76 | a, b := x.(int64), y.(int64) 77 | switch { 78 | case a > b: 79 | return 1 80 | case b > a: 81 | return -1 82 | default: 83 | return 0 84 | } 85 | } 86 | case uint: 87 | return func(x, y interface{}) int { 88 | a, b := x.(uint), y.(uint) 89 | switch { 90 | case a > b: 91 | return 1 92 | case b > a: 93 | return -1 94 | default: 95 | return 0 96 | } 97 | } 98 | case uint8: 99 | return func(x, y interface{}) int { 100 | a, b := x.(uint8), y.(uint8) 101 | switch { 102 | case a > b: 103 | return 1 104 | case b > a: 105 | return -1 106 | default: 107 | return 0 108 | } 109 | } 110 | case uint16: 111 | return func(x, y interface{}) int { 112 | a, b := x.(uint16), y.(uint16) 113 | switch { 114 | case a > b: 115 | return 1 116 | case b > a: 117 | return -1 118 | default: 119 | return 0 120 | } 121 | } 122 | case uint32: 123 | return func(x, y interface{}) int { 124 | a, b := x.(uint32), y.(uint32) 125 | switch { 126 | case a > b: 127 | return 1 128 | case b > a: 129 | return -1 130 | default: 131 | return 0 132 | } 133 | } 134 | case uint64: 135 | return func(x, y interface{}) int { 136 | a, b := x.(uint64), y.(uint64) 137 | switch { 138 | case a > b: 139 | return 1 140 | case b > a: 141 | return -1 142 | default: 143 | return 0 144 | } 145 | } 146 | case float32: 147 | return func(x, y interface{}) int { 148 | a, b := x.(float32), y.(float32) 149 | switch { 150 | case a > b: 151 | return 1 152 | case b > a: 153 | return -1 154 | default: 155 | return 0 156 | } 157 | } 158 | case float64: 159 | return func(x, y interface{}) int { 160 | a, b := x.(float64), y.(float64) 161 | switch { 162 | case a > b: 163 | return 1 164 | case b > a: 165 | return -1 166 | default: 167 | return 0 168 | } 169 | } 170 | case string: 171 | return func(x, y interface{}) int { 172 | a, b := x.(string), y.(string) 173 | switch { 174 | case a > b: 175 | return 1 176 | case b > a: 177 | return -1 178 | default: 179 | return 0 180 | } 181 | } 182 | case bool: 183 | return func(x, y interface{}) int { 184 | a, b := x.(bool), y.(bool) 185 | switch { 186 | case a == b: 187 | return 0 188 | case a: 189 | return 1 190 | default: 191 | return -1 192 | } 193 | } 194 | default: 195 | return func(x, y interface{}) int { 196 | a, b := x.(Comparable), y.(Comparable) 197 | return a.CompareTo(b) 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /compare_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestGetComparer(t *testing.T) { 6 | tests := []struct { 7 | x interface{} 8 | y interface{} 9 | want int 10 | }{ 11 | {100, 500, -1}, 12 | {-100, -500, 1}, 13 | {256, 256, 0}, 14 | {int8(100), int8(-100), 1}, 15 | {int8(-100), int8(100), -1}, 16 | {int8(100), int8(100), 0}, 17 | {int16(100), int16(-100), 1}, 18 | {int16(-100), int16(100), -1}, 19 | {int16(100), int16(100), 0}, 20 | {int32(100), int32(-100), 1}, 21 | {int32(-100), int32(100), -1}, 22 | {int32(100), int32(100), 0}, 23 | {int64(100), int64(-100), 1}, 24 | {int64(-100), int64(100), -1}, 25 | {int64(100), int64(100), 0}, 26 | {uint(100), uint(0), 1}, 27 | {uint(0), uint(100), -1}, 28 | {uint(100), uint(100), 0}, 29 | {uint8(100), uint8(0), 1}, 30 | {uint8(0), uint8(100), -1}, 31 | {uint8(100), uint8(100), 0}, 32 | {uint16(100), uint16(0), 1}, 33 | {uint16(0), uint16(100), -1}, 34 | {uint16(100), uint16(100), 0}, 35 | {uint32(100), uint32(0), 1}, 36 | {uint32(0), uint32(100), -1}, 37 | {uint32(100), uint32(100), 0}, 38 | {uint64(100), uint64(0), 1}, 39 | {uint64(0), uint64(100), -1}, 40 | {uint64(100), uint64(100), 0}, 41 | {float32(5.), float32(1.), 1}, 42 | {float32(1.), float32(5.), -1}, 43 | {float32(0), float32(0), 0}, 44 | {float64(5.), float64(1.), 1}, 45 | {float64(1.), float64(5.), -1}, 46 | {float64(0), float64(0), 0}, 47 | {true, true, 0}, 48 | {false, false, 0}, 49 | {true, false, 1}, 50 | {false, true, -1}, 51 | {"foo", "foo", 0}, 52 | {"foo", "bar", 1}, 53 | {"bar", "foo", -1}, 54 | {"FOO", "bar", -1}, 55 | {foo{f1: 1}, foo{f1: 5}, -1}, 56 | {foo{f1: 5}, foo{f1: 1}, 1}, 57 | {foo{f1: 1}, foo{f1: 1}, 0}, 58 | } 59 | 60 | for _, test := range tests { 61 | if r := getComparer(test.x)(test.x, test.y); r != test.want { 62 | t.Errorf("getComparer(%v)(%v,%v)=%v expected %v", test.x, test.x, test.y, r, test.want) 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /concat.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Append inserts an item to the end of a collection, so it becomes the last 4 | // item. 5 | func (q Query) Append(item interface{}) Query { 6 | return Query{ 7 | Iterate: func() Iterator { 8 | next := q.Iterate() 9 | appended := false 10 | 11 | return func() (interface{}, bool) { 12 | i, ok := next() 13 | if ok { 14 | return i, ok 15 | } 16 | 17 | if !appended { 18 | appended = true 19 | return item, true 20 | } 21 | 22 | return nil, false 23 | } 24 | }, 25 | } 26 | } 27 | 28 | // Concat concatenates two collections. 29 | // 30 | // The Concat method differs from the Union method because the Concat method 31 | // returns all the original elements in the input sequences. The Union method 32 | // returns only unique elements. 33 | func (q Query) Concat(q2 Query) Query { 34 | return Query{ 35 | Iterate: func() Iterator { 36 | next := q.Iterate() 37 | next2 := q2.Iterate() 38 | use1 := true 39 | 40 | return func() (item interface{}, ok bool) { 41 | if use1 { 42 | item, ok = next() 43 | if ok { 44 | return 45 | } 46 | 47 | use1 = false 48 | } 49 | 50 | return next2() 51 | } 52 | }, 53 | } 54 | } 55 | 56 | // Prepend inserts an item to the beginning of a collection, so it becomes the 57 | // first item. 58 | func (q Query) Prepend(item interface{}) Query { 59 | return Query{ 60 | Iterate: func() Iterator { 61 | next := q.Iterate() 62 | prepended := false 63 | 64 | return func() (interface{}, bool) { 65 | if prepended { 66 | return next() 67 | } 68 | 69 | prepended = true 70 | return item, true 71 | } 72 | }, 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /concat_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestAppend(t *testing.T) { 6 | input := []int{1, 2, 3, 4} 7 | want := []interface{}{1, 2, 3, 4, 5} 8 | 9 | if q := From(input).Append(5); !validateQuery(q, want) { 10 | t.Errorf("From(%v).Append()=%v expected %v", input, toSlice(q), want) 11 | } 12 | } 13 | 14 | func TestConcat(t *testing.T) { 15 | input1 := []int{1, 2, 3} 16 | input2 := []int{4, 5} 17 | want := []interface{}{1, 2, 3, 4, 5} 18 | 19 | if q := From(input1).Concat(From(input2)); !validateQuery(q, want) { 20 | t.Errorf("From(%v).Concat(%v)=%v expected %v", input1, input2, toSlice(q), want) 21 | } 22 | } 23 | 24 | func TestPrepend(t *testing.T) { 25 | input := []int{1, 2, 3, 4} 26 | want := []interface{}{0, 1, 2, 3, 4} 27 | 28 | if q := From(input).Prepend(0); !validateQuery(q, want) { 29 | t.Errorf("From(%v).Prepend()=%v expected %v", input, toSlice(q), want) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /convert.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | type intConverter func(interface{}) int64 4 | 5 | func getIntConverter(data interface{}) intConverter { 6 | switch data.(type) { 7 | case (int): 8 | return func(i interface{}) int64 { 9 | return int64(i.(int)) 10 | } 11 | case (int8): 12 | return func(i interface{}) int64 { 13 | return int64(i.(int8)) 14 | } 15 | case (int16): 16 | return func(i interface{}) int64 { 17 | return int64(i.(int16)) 18 | } 19 | case (int32): 20 | return func(i interface{}) int64 { 21 | return int64(i.(int32)) 22 | } 23 | } 24 | 25 | return func(i interface{}) int64 { 26 | return i.(int64) 27 | } 28 | } 29 | 30 | type uintConverter func(interface{}) uint64 31 | 32 | func getUIntConverter(data interface{}) uintConverter { 33 | switch data.(type) { 34 | case (uint): 35 | return func(i interface{}) uint64 { 36 | return uint64(i.(uint)) 37 | } 38 | case (uint8): 39 | return func(i interface{}) uint64 { 40 | return uint64(i.(uint8)) 41 | } 42 | case (uint16): 43 | return func(i interface{}) uint64 { 44 | return uint64(i.(uint16)) 45 | } 46 | case (uint32): 47 | return func(i interface{}) uint64 { 48 | return uint64(i.(uint32)) 49 | } 50 | } 51 | 52 | return func(i interface{}) uint64 { 53 | return i.(uint64) 54 | } 55 | } 56 | 57 | type floatConverter func(interface{}) float64 58 | 59 | func getFloatConverter(data interface{}) floatConverter { 60 | switch data.(type) { 61 | case (float32): 62 | return func(i interface{}) float64 { 63 | return float64(i.(float32)) 64 | } 65 | } 66 | 67 | return func(i interface{}) float64 { 68 | return i.(float64) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /convert_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestIntConverter(t *testing.T) { 6 | tests := []struct { 7 | input interface{} 8 | want int64 9 | }{ 10 | {2, 2}, 11 | {int8(-1), -1}, 12 | {int16(0), 0}, 13 | {int32(10), 10}, 14 | {int64(5), 5}, 15 | } 16 | 17 | for _, test := range tests { 18 | if conv := getIntConverter(test.input); conv(test.input) != test.want { 19 | t.Errorf("IntConverter for %v failed", test.input) 20 | } 21 | } 22 | } 23 | 24 | func TestUIntConverter(t *testing.T) { 25 | tests := []struct { 26 | input interface{} 27 | want uint64 28 | }{ 29 | {uint(2), 2}, 30 | {uint8(1), 1}, 31 | {uint16(0), 0}, 32 | {uint32(10), 10}, 33 | {uint64(5), 5}, 34 | } 35 | 36 | for _, test := range tests { 37 | if conv := getUIntConverter(test.input); conv(test.input) != test.want { 38 | t.Errorf("UIntConverter for %v failed", test.input) 39 | } 40 | } 41 | } 42 | 43 | func TestFloatConverter(t *testing.T) { 44 | tests := []struct { 45 | input interface{} 46 | want float64 47 | }{ 48 | {float32(-1), -1}, 49 | {float64(0), 0}, 50 | } 51 | 52 | for _, test := range tests { 53 | if conv := getFloatConverter(test.input); conv(test.input) != test.want { 54 | t.Errorf("FloatConverter for %v failed", test.input) 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /defaultifempty.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // DefaultIfEmpty returns the elements of the specified sequence 4 | // if the sequence is empty. 5 | func (q Query) DefaultIfEmpty(defaultValue interface{}) Query { 6 | return Query{ 7 | Iterate: func() Iterator { 8 | next := q.Iterate() 9 | state := 1 10 | 11 | return func() (item interface{}, ok bool) { 12 | switch state { 13 | case 1: 14 | item, ok = next() 15 | if ok { 16 | state = 2 17 | } else { 18 | item = defaultValue 19 | ok = true 20 | state = -1 21 | } 22 | return 23 | case 2: 24 | for item, ok = next(); ok; item, ok = next() { 25 | return 26 | } 27 | return 28 | } 29 | return 30 | } 31 | }, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /defaultifempty_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestDefaultIfEmpty(t *testing.T) { 8 | defaultValue := 0 9 | tests := []struct { 10 | input []interface{} 11 | want []interface{} 12 | }{ 13 | {[]interface{}{}, []interface{}{defaultValue}}, 14 | {[]interface{}{1, 2, 3, 4, 5}, []interface{}{1, 2, 3, 4, 5}}, 15 | } 16 | 17 | for _, test := range tests { 18 | q := From(test.input).DefaultIfEmpty(defaultValue) 19 | 20 | if !validateQuery(q, test.want) { 21 | t.Errorf("From(%v).DefaultIfEmpty(%v)=%v expected %v", test.input, defaultValue, toSlice(q), test.want) 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /distinct.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Distinct method returns distinct elements from a collection. The result is an 4 | // unordered collection that contains no duplicate values. 5 | func (q Query) Distinct() Query { 6 | return Query{ 7 | Iterate: func() Iterator { 8 | next := q.Iterate() 9 | set := make(map[interface{}]bool) 10 | 11 | return func() (item interface{}, ok bool) { 12 | for item, ok = next(); ok; item, ok = next() { 13 | if _, has := set[item]; !has { 14 | set[item] = true 15 | return 16 | } 17 | } 18 | 19 | return 20 | } 21 | }, 22 | } 23 | } 24 | 25 | // Distinct method returns distinct elements from a collection. The result is an 26 | // ordered collection that contains no duplicate values. 27 | // 28 | // NOTE: Distinct method on OrderedQuery type has better performance than 29 | // Distinct method on Query type. 30 | func (oq OrderedQuery) Distinct() OrderedQuery { 31 | return OrderedQuery{ 32 | orders: oq.orders, 33 | Query: Query{ 34 | Iterate: func() Iterator { 35 | next := oq.Iterate() 36 | var prev interface{} 37 | 38 | return func() (item interface{}, ok bool) { 39 | for item, ok = next(); ok; item, ok = next() { 40 | if item != prev { 41 | prev = item 42 | return 43 | } 44 | } 45 | 46 | return 47 | } 48 | }, 49 | }, 50 | } 51 | } 52 | 53 | // DistinctBy method returns distinct elements from a collection. This method 54 | // executes selector function for each element to determine a value to compare. 55 | // The result is an unordered collection that contains no duplicate values. 56 | func (q Query) DistinctBy(selector func(interface{}) interface{}) Query { 57 | return Query{ 58 | Iterate: func() Iterator { 59 | next := q.Iterate() 60 | set := make(map[interface{}]bool) 61 | 62 | return func() (item interface{}, ok bool) { 63 | for item, ok = next(); ok; item, ok = next() { 64 | s := selector(item) 65 | if _, has := set[s]; !has { 66 | set[s] = true 67 | return 68 | } 69 | } 70 | 71 | return 72 | } 73 | }, 74 | } 75 | } 76 | 77 | // DistinctByT is the typed version of DistinctBy. 78 | // 79 | // - selectorFn is of type "func(TSource) TSource". 80 | // 81 | // NOTE: DistinctBy has better performance than DistinctByT. 82 | func (q Query) DistinctByT(selectorFn interface{}) Query { 83 | selectorFunc, ok := selectorFn.(func(interface{}) interface{}) 84 | if !ok { 85 | selectorGenericFunc, err := newGenericFunc( 86 | "DistinctByT", "selectorFn", selectorFn, 87 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 88 | ) 89 | if err != nil { 90 | panic(err) 91 | } 92 | 93 | selectorFunc = func(item interface{}) interface{} { 94 | return selectorGenericFunc.Call(item) 95 | } 96 | } 97 | return q.DistinctBy(selectorFunc) 98 | } 99 | -------------------------------------------------------------------------------- /distinct_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestDistinct(t *testing.T) { 6 | tests := []struct { 7 | input interface{} 8 | output []interface{} 9 | }{ 10 | {[]int{1, 2, 2, 3, 1}, []interface{}{1, 2, 3}}, 11 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, []interface{}{1, 2, 3, 4}}, 12 | {"sstr", []interface{}{'s', 't', 'r'}}, 13 | } 14 | 15 | for _, test := range tests { 16 | if q := From(test.input).Distinct(); !validateQuery(q, test.output) { 17 | t.Errorf("From(%v).Distinct()=%v expected %v", test.input, toSlice(q), test.output) 18 | } 19 | } 20 | } 21 | 22 | func TestDistinctForOrderedQuery(t *testing.T) { 23 | tests := []struct { 24 | input interface{} 25 | output []interface{} 26 | }{ 27 | {[]int{1, 2, 2, 3, 1}, []interface{}{1, 2, 3}}, 28 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, []interface{}{1, 2, 3, 4}}, 29 | {"sstr", []interface{}{'r', 's', 't'}}, 30 | } 31 | 32 | for _, test := range tests { 33 | if q := From(test.input).OrderBy(func(i interface{}) interface{} { 34 | return i 35 | }).Distinct(); !validateQuery(q.Query, test.output) { 36 | t.Errorf("From(%v).Distinct()=%v expected %v", test.input, toSlice(q.Query), test.output) 37 | } 38 | } 39 | } 40 | 41 | func TestDistinctBy(t *testing.T) { 42 | type user struct { 43 | id int 44 | name string 45 | } 46 | 47 | users := []user{{1, "Foo"}, {2, "Bar"}, {3, "Foo"}} 48 | want := []interface{}{user{1, "Foo"}, user{2, "Bar"}} 49 | 50 | if q := From(users).DistinctBy(func(u interface{}) interface{} { 51 | return u.(user).name 52 | }); !validateQuery(q, want) { 53 | t.Errorf("From(%v).DistinctBy()=%v expected %v", users, toSlice(q), want) 54 | } 55 | } 56 | 57 | func TestDistinctByT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 58 | mustPanicWithError(t, "DistinctByT: parameter [selectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(string,string)bool'", func() { 59 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).DistinctByT(func(indice, item string) bool { return item == "2" }) 60 | }) 61 | } 62 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Package linq provides methods for querying and manipulating slices, arrays, 2 | // maps, strings, channels and collections. 3 | // 4 | // Authors: Alexander Kalankhodzhaev (kalan), Ahmet Alp Balkan, Cleiton Marques 5 | // Souza. 6 | package linq 7 | -------------------------------------------------------------------------------- /except.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Except produces the set difference of two sequences. The set difference is 4 | // the members of the first sequence that don't appear in the second sequence. 5 | func (q Query) Except(q2 Query) Query { 6 | return Query{ 7 | Iterate: func() Iterator { 8 | next := q.Iterate() 9 | 10 | next2 := q2.Iterate() 11 | set := make(map[interface{}]bool) 12 | for i, ok := next2(); ok; i, ok = next2() { 13 | set[i] = true 14 | } 15 | 16 | return func() (item interface{}, ok bool) { 17 | for item, ok = next(); ok; item, ok = next() { 18 | if _, has := set[item]; !has { 19 | return 20 | } 21 | } 22 | 23 | return 24 | } 25 | }, 26 | } 27 | } 28 | 29 | // ExceptBy invokes a transform function on each element of a collection and 30 | // produces the set difference of two sequences. The set difference is the 31 | // members of the first sequence that don't appear in the second sequence. 32 | func (q Query) ExceptBy(q2 Query, 33 | selector func(interface{}) interface{}) Query { 34 | return Query{ 35 | Iterate: func() Iterator { 36 | next := q.Iterate() 37 | 38 | next2 := q2.Iterate() 39 | set := make(map[interface{}]bool) 40 | for i, ok := next2(); ok; i, ok = next2() { 41 | s := selector(i) 42 | set[s] = true 43 | } 44 | 45 | return func() (item interface{}, ok bool) { 46 | for item, ok = next(); ok; item, ok = next() { 47 | s := selector(item) 48 | if _, has := set[s]; !has { 49 | return 50 | } 51 | } 52 | 53 | return 54 | } 55 | }, 56 | } 57 | } 58 | 59 | // ExceptByT is the typed version of ExceptBy. 60 | // 61 | // - selectorFn is of type "func(TSource) TSource" 62 | // 63 | // NOTE: ExceptBy has better performance than ExceptByT. 64 | func (q Query) ExceptByT(q2 Query, 65 | selectorFn interface{}) Query { 66 | selectorGenericFunc, err := newGenericFunc( 67 | "ExceptByT", "selectorFn", selectorFn, 68 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 69 | ) 70 | if err != nil { 71 | panic(err) 72 | } 73 | 74 | selectorFunc := func(item interface{}) interface{} { 75 | return selectorGenericFunc.Call(item) 76 | } 77 | 78 | return q.ExceptBy(q2, selectorFunc) 79 | } 80 | -------------------------------------------------------------------------------- /except_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestExcept(t *testing.T) { 6 | input1 := []int{1, 2, 3, 4, 5, 1, 2, 5} 7 | input2 := []int{1, 2} 8 | want := []interface{}{3, 4, 5, 5} 9 | 10 | if q := From(input1).Except(From(input2)); !validateQuery(q, want) { 11 | t.Errorf("From(%v).Except(%v)=%v expected %v", input1, input2, toSlice(q), want) 12 | } 13 | } 14 | 15 | func TestExceptBy(t *testing.T) { 16 | input1 := []int{1, 2, 3, 4, 5, 1, 2, 5} 17 | input2 := []int{1} 18 | want := []interface{}{2, 4, 2} 19 | 20 | if q := From(input1).ExceptBy(From(input2), func(i interface{}) interface{} { 21 | return i.(int) % 2 22 | }); !validateQuery(q, want) { 23 | t.Errorf("From(%v).ExceptBy(%v)=%v expected %v", input1, input2, toSlice(q), want) 24 | } 25 | } 26 | 27 | func TestExceptByT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 28 | mustPanicWithError(t, "ExceptByT: parameter [selectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 29 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).ExceptByT(From([]int{1}), func(x, item int) int { return item + 2 }) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /from.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "reflect" 4 | 5 | // Iterator is an alias for function to iterate over data. 6 | type Iterator func() (item interface{}, ok bool) 7 | 8 | // Query is the type returned from query functions. It can be iterated manually 9 | // as shown in the example. 10 | type Query struct { 11 | Iterate func() Iterator 12 | } 13 | 14 | // KeyValue is a type that is used to iterate over a map (if query is created 15 | // from a map). This type is also used by ToMap() method to output result of a 16 | // query into a map. 17 | type KeyValue struct { 18 | Key interface{} 19 | Value interface{} 20 | } 21 | 22 | // Iterable is an interface that has to be implemented by a custom collection in 23 | // order to work with linq. 24 | type Iterable interface { 25 | Iterate() Iterator 26 | } 27 | 28 | // From initializes a linq query with passed slice, array or map as the source. 29 | // String, channel or struct implementing Iterable interface can be used as an 30 | // input. In this case From delegates it to FromString, FromChannel and 31 | // FromIterable internally. 32 | func From(source interface{}) Query { 33 | src := reflect.ValueOf(source) 34 | 35 | switch src.Kind() { 36 | case reflect.Slice, reflect.Array: 37 | len := src.Len() 38 | 39 | return Query{ 40 | Iterate: func() Iterator { 41 | index := 0 42 | 43 | return func() (item interface{}, ok bool) { 44 | ok = index < len 45 | if ok { 46 | item = src.Index(index).Interface() 47 | index++ 48 | } 49 | 50 | return 51 | } 52 | }, 53 | } 54 | case reflect.Map: 55 | len := src.Len() 56 | 57 | return Query{ 58 | Iterate: func() Iterator { 59 | index := 0 60 | keys := src.MapKeys() 61 | 62 | return func() (item interface{}, ok bool) { 63 | ok = index < len 64 | if ok { 65 | key := keys[index] 66 | item = KeyValue{ 67 | Key: key.Interface(), 68 | Value: src.MapIndex(key).Interface(), 69 | } 70 | 71 | index++ 72 | } 73 | 74 | return 75 | } 76 | }, 77 | } 78 | case reflect.String: 79 | return FromString(source.(string)) 80 | case reflect.Chan: 81 | if _, ok := source.(chan interface{}); ok { 82 | return FromChannel(source.(chan interface{})) 83 | } else { 84 | return FromChannelT(source) 85 | } 86 | default: 87 | return FromIterable(source.(Iterable)) 88 | } 89 | } 90 | 91 | // FromChannel initializes a linq query with passed channel, linq iterates over 92 | // channel until it is closed. 93 | func FromChannel(source <-chan interface{}) Query { 94 | return Query{ 95 | Iterate: func() Iterator { 96 | return func() (item interface{}, ok bool) { 97 | item, ok = <-source 98 | return 99 | } 100 | }, 101 | } 102 | } 103 | 104 | // FromChannelT is the typed version of FromChannel. 105 | // 106 | // - source is of type "chan TSource" 107 | // 108 | // NOTE: FromChannel has better performance than FromChannelT. 109 | func FromChannelT(source interface{}) Query { 110 | src := reflect.ValueOf(source) 111 | return Query{ 112 | Iterate: func() Iterator { 113 | return func() (interface{}, bool) { 114 | value, ok := src.Recv() 115 | return value.Interface(), ok 116 | } 117 | }, 118 | } 119 | } 120 | 121 | // FromString initializes a linq query with passed string, linq iterates over 122 | // runes of string. 123 | func FromString(source string) Query { 124 | runes := []rune(source) 125 | len := len(runes) 126 | 127 | return Query{ 128 | Iterate: func() Iterator { 129 | index := 0 130 | 131 | return func() (item interface{}, ok bool) { 132 | ok = index < len 133 | if ok { 134 | item = runes[index] 135 | index++ 136 | } 137 | 138 | return 139 | } 140 | }, 141 | } 142 | } 143 | 144 | // FromIterable initializes a linq query with custom collection passed. This 145 | // collection has to implement Iterable interface, linq iterates over items, 146 | // that has to implement Comparable interface or be basic types. 147 | func FromIterable(source Iterable) Query { 148 | return Query{ 149 | Iterate: source.Iterate, 150 | } 151 | } 152 | 153 | // Range generates a sequence of integral numbers within a specified range. 154 | func Range(start, count int) Query { 155 | return Query{ 156 | Iterate: func() Iterator { 157 | index := 0 158 | current := start 159 | 160 | return func() (item interface{}, ok bool) { 161 | if index >= count { 162 | return nil, false 163 | } 164 | 165 | item, ok = current, true 166 | 167 | index++ 168 | current++ 169 | return 170 | } 171 | }, 172 | } 173 | } 174 | 175 | // Repeat generates a sequence that contains one repeated value. 176 | func Repeat(value interface{}, count int) Query { 177 | return Query{ 178 | Iterate: func() Iterator { 179 | index := 0 180 | 181 | return func() (item interface{}, ok bool) { 182 | if index >= count { 183 | return nil, false 184 | } 185 | 186 | item, ok = value, true 187 | 188 | index++ 189 | return 190 | } 191 | }, 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /from_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestFrom(t *testing.T) { 6 | c := make(chan interface{}, 3) 7 | c <- -1 8 | c <- 0 9 | c <- 1 10 | close(c) 11 | 12 | ct := make(chan int, 3) 13 | ct <- -10 14 | ct <- 0 15 | ct <- 10 16 | close(ct) 17 | 18 | tests := []struct { 19 | input interface{} 20 | output []interface{} 21 | want bool 22 | }{ 23 | {[]int{1, 2, 3}, []interface{}{1, 2, 3}, true}, 24 | {[]int{1, 2, 4}, []interface{}{1, 2, 3}, false}, 25 | {[3]int{1, 2, 3}, []interface{}{1, 2, 3}, true}, 26 | {[3]int{1, 2, 4}, []interface{}{1, 2, 3}, false}, 27 | {"str", []interface{}{'s', 't', 'r'}, true}, 28 | {"str", []interface{}{'s', 't', 'g'}, false}, 29 | {map[string]bool{"foo": true}, []interface{}{KeyValue{"foo", true}}, true}, 30 | {map[string]bool{"foo": true}, []interface{}{KeyValue{"foo", false}}, false}, 31 | {c, []interface{}{-1, 0, 1}, true}, 32 | {ct, []interface{}{-10, 0, 10}, true}, 33 | {foo{f1: 1, f2: true, f3: "string"}, []interface{}{1, true, "string"}, true}, 34 | } 35 | 36 | for _, test := range tests { 37 | if q := From(test.input); validateQuery(q, test.output) != test.want { 38 | if test.want { 39 | t.Errorf("From(%v)=%v expected %v", test.input, toSlice(q), test.output) 40 | } else { 41 | t.Errorf("From(%v)=%v expected not equal", test.input, test.output) 42 | } 43 | } 44 | } 45 | } 46 | 47 | func TestFromChannel(t *testing.T) { 48 | c := make(chan interface{}, 3) 49 | c <- 10 50 | c <- 15 51 | c <- -3 52 | close(c) 53 | 54 | w := []interface{}{10, 15, -3} 55 | 56 | if q := FromChannel(c); !validateQuery(q, w) { 57 | t.Errorf("FromChannel() failed expected %v", w) 58 | } 59 | } 60 | 61 | func TestFromChannelT(t *testing.T) { 62 | c := make(chan int, 3) 63 | c <- 10 64 | c <- 15 65 | c <- -3 66 | close(c) 67 | 68 | w := []interface{}{10, 15, -3} 69 | 70 | if q := FromChannelT(c); !validateQuery(q, w) { 71 | t.Errorf("FromChannelT() failed expected %v", w) 72 | } 73 | } 74 | 75 | func TestFromString(t *testing.T) { 76 | s := "string" 77 | w := []interface{}{'s', 't', 'r', 'i', 'n', 'g'} 78 | 79 | if q := FromString(s); !validateQuery(q, w) { 80 | t.Errorf("FromString(%v)!=%v", s, w) 81 | } 82 | } 83 | 84 | func TestFromIterable(t *testing.T) { 85 | s := foo{f1: 1, f2: true, f3: "string"} 86 | w := []interface{}{1, true, "string"} 87 | 88 | if q := FromIterable(s); !validateQuery(q, w) { 89 | t.Errorf("FromIterable(%v)!=%v", s, w) 90 | } 91 | } 92 | 93 | func TestRange(t *testing.T) { 94 | w := []interface{}{-2, -1, 0, 1, 2} 95 | 96 | if q := Range(-2, 5); !validateQuery(q, w) { 97 | t.Errorf("Range(-2, 5)=%v expected %v", toSlice(q), w) 98 | } 99 | } 100 | 101 | func TestRepeat(t *testing.T) { 102 | w := []interface{}{1, 1, 1, 1, 1} 103 | 104 | if q := Repeat(1, 5); !validateQuery(q, w) { 105 | t.Errorf("Repeat(1, 5)=%v expected %v", toSlice(q), w) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /general_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestChannelToChannel(t *testing.T) { 9 | input := []int{30, 40, 50} 10 | 11 | inpCh := make(chan interface{}) 12 | resCh := make(chan interface{}) 13 | 14 | go func() { 15 | for _, i := range input { 16 | inpCh <- i 17 | } 18 | 19 | close(inpCh) 20 | }() 21 | 22 | go func() { 23 | FromChannel(inpCh).Where(func(i interface{}) bool { 24 | return i.(int) > 20 25 | }).ToChannel(resCh) 26 | }() 27 | 28 | result := []int{} 29 | for value := range resCh { 30 | result = append(result, value.(int)) 31 | } 32 | 33 | if !reflect.DeepEqual(result, input) { 34 | t.Errorf("FromChannel().ToChannel()=%v expected %v", result, input) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /genericfunc.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | // genericType represents a any reflect.Type. 10 | type genericType int 11 | 12 | var genericTp = reflect.TypeOf(new(genericType)).Elem() 13 | 14 | // functionCache keeps genericFunc reflection objects in cache. 15 | type functionCache struct { 16 | MethodName string 17 | ParamName string 18 | FnValue reflect.Value 19 | FnType reflect.Type 20 | TypesIn []reflect.Type 21 | TypesOut []reflect.Type 22 | } 23 | 24 | // genericFunc is a type used to validate and call dynamic functions. 25 | type genericFunc struct { 26 | Cache *functionCache 27 | } 28 | 29 | // Call calls a dynamic function. 30 | func (g *genericFunc) Call(params ...interface{}) interface{} { 31 | paramsIn := make([]reflect.Value, len(params)) 32 | for i, param := range params { 33 | paramsIn[i] = reflect.ValueOf(param) 34 | } 35 | paramsOut := g.Cache.FnValue.Call(paramsIn) 36 | if len(paramsOut) >= 1 { 37 | return paramsOut[0].Interface() 38 | } 39 | return nil 40 | } 41 | 42 | // newGenericFunc instantiates a new genericFunc pointer 43 | func newGenericFunc(methodName, paramName string, fn interface{}, validateFunc func(*functionCache) error) (*genericFunc, error) { 44 | cache := &functionCache{} 45 | cache.FnValue = reflect.ValueOf(fn) 46 | 47 | if cache.FnValue.Kind() != reflect.Func { 48 | return nil, fmt.Errorf("%s: parameter [%s] is not a function type. It is a '%s'", methodName, paramName, cache.FnValue.Type()) 49 | } 50 | cache.MethodName = methodName 51 | cache.ParamName = paramName 52 | cache.FnType = cache.FnValue.Type() 53 | numTypesIn := cache.FnType.NumIn() 54 | cache.TypesIn = make([]reflect.Type, numTypesIn) 55 | for i := 0; i < numTypesIn; i++ { 56 | cache.TypesIn[i] = cache.FnType.In(i) 57 | } 58 | 59 | numTypesOut := cache.FnType.NumOut() 60 | cache.TypesOut = make([]reflect.Type, numTypesOut) 61 | for i := 0; i < numTypesOut; i++ { 62 | cache.TypesOut[i] = cache.FnType.Out(i) 63 | } 64 | if err := validateFunc(cache); err != nil { 65 | return nil, err 66 | } 67 | 68 | return &genericFunc{Cache: cache}, nil 69 | } 70 | 71 | // simpleParamValidator creates a function to validate genericFunc based in the 72 | // In and Out function parameters. 73 | func simpleParamValidator(In []reflect.Type, Out []reflect.Type) func(cache *functionCache) error { 74 | return func(cache *functionCache) error { 75 | var isValid = func() bool { 76 | if In != nil { 77 | if len(In) != len(cache.TypesIn) { 78 | return false 79 | } 80 | for i, paramIn := range In { 81 | if paramIn != genericTp && paramIn != cache.TypesIn[i] { 82 | return false 83 | } 84 | } 85 | } 86 | if Out != nil { 87 | if len(Out) != len(cache.TypesOut) { 88 | return false 89 | } 90 | for i, paramOut := range Out { 91 | if paramOut != genericTp && paramOut != cache.TypesOut[i] { 92 | return false 93 | } 94 | } 95 | } 96 | return true 97 | } 98 | 99 | if !isValid() { 100 | return fmt.Errorf("%s: parameter [%s] has a invalid function signature. Expected: '%s', actual: '%s'", cache.MethodName, cache.ParamName, formatFnSignature(In, Out), formatFnSignature(cache.TypesIn, cache.TypesOut)) 101 | } 102 | return nil 103 | } 104 | } 105 | 106 | // newElemTypeSlice creates a slice of items elem types. 107 | func newElemTypeSlice(items ...interface{}) []reflect.Type { 108 | typeList := make([]reflect.Type, len(items)) 109 | for i, item := range items { 110 | typeItem := reflect.TypeOf(item) 111 | if typeItem.Kind() == reflect.Ptr { 112 | typeList[i] = typeItem.Elem() 113 | } 114 | } 115 | return typeList 116 | } 117 | 118 | // formatFnSignature formats the func signature based in the parameters types. 119 | func formatFnSignature(In []reflect.Type, Out []reflect.Type) string { 120 | paramInNames := make([]string, len(In)) 121 | for i, typeIn := range In { 122 | if typeIn == genericTp { 123 | paramInNames[i] = "T" 124 | } else { 125 | paramInNames[i] = typeIn.String() 126 | } 127 | 128 | } 129 | paramOutNames := make([]string, len(Out)) 130 | for i, typeOut := range Out { 131 | if typeOut == genericTp { 132 | paramOutNames[i] = "T" 133 | } else { 134 | paramOutNames[i] = typeOut.String() 135 | } 136 | } 137 | return fmt.Sprintf("func(%s)%s", strings.Join(paramInNames, ","), strings.Join(paramOutNames, ",")) 138 | } 139 | -------------------------------------------------------------------------------- /genericfunc_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func TestNewGenericFunc(t *testing.T) { 10 | tests := []struct { 11 | methodName string 12 | paramName string 13 | function interface{} 14 | validationFunc func(*functionCache) error 15 | exception error 16 | }{ 17 | { // A valid function 18 | "TestNewGenericFunc", "test1", 19 | func(item int) bool { return item > 10 }, 20 | simpleParamValidator(newElemTypeSlice(new(int)), newElemTypeSlice(new(bool))), 21 | nil, 22 | }, 23 | { // A valid generic function 24 | "TestNewGenericFunc", "test1", 25 | func(item int) bool { return item > 10 }, 26 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 27 | nil, 28 | }, 29 | { //returns error when the function parameter has not the function kind 30 | "TestNewGenericFunc", "test2", 31 | "Not a function", 32 | simpleParamValidator(nil, []reflect.Type{}), 33 | errors.New("TestNewGenericFunc: parameter [test2] is not a function type. It is a 'string'"), 34 | }, 35 | { // Returns error when expected parameters number are not equal 36 | "TestNewGenericFunc", "test3", 37 | func(idx, item int) {}, 38 | simpleParamValidator(newElemTypeSlice(new(int)), []reflect.Type{}), 39 | errors.New("TestNewGenericFunc: parameter [test3] has a invalid function signature. Expected: 'func(int)', actual: 'func(int,int)'"), 40 | }, 41 | { // Returns error when expected parameters types are not equal 42 | "TestNewGenericFunc", "test4", 43 | func(items ...int) bool { return false }, 44 | simpleParamValidator(newElemTypeSlice(new([]bool)), newElemTypeSlice(new(bool))), 45 | errors.New("TestNewGenericFunc: parameter [test4] has a invalid function signature. Expected: 'func([]bool)bool', actual: 'func([]int)bool'"), 46 | }, 47 | { // Returns error when expected returns number are not equal 48 | "TestNewGenericFunc", "test5", 49 | func(item int) bool { return item > 10 }, 50 | simpleParamValidator(newElemTypeSlice(new(int)), []reflect.Type{}), 51 | errors.New("TestNewGenericFunc: parameter [test5] has a invalid function signature. Expected: 'func(int)', actual: 'func(int)bool'"), 52 | }, 53 | { // Returns error when expected return types are not equal 54 | "TestNewGenericFunc", "test6", 55 | func(items ...int) bool { return false }, 56 | simpleParamValidator(newElemTypeSlice(new([]int)), newElemTypeSlice(new(int64))), 57 | errors.New("TestNewGenericFunc: parameter [test6] has a invalid function signature. Expected: 'func([]int)int64', actual: 'func([]int)bool'"), 58 | }, 59 | { // Returns error when expected return types are not equal 60 | "TestNewGenericFunc", "test7", 61 | func(items ...int) bool { return false }, 62 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(int64))), 63 | errors.New("TestNewGenericFunc: parameter [test7] has a invalid function signature. Expected: 'func(T)int64', actual: 'func([]int)bool'"), 64 | }, 65 | } 66 | 67 | for _, test := range tests { 68 | _, err := newGenericFunc(test.methodName, test.paramName, test.function, test.validationFunc) 69 | if !(err == test.exception || err.Error() == test.exception.Error()) { 70 | t.Errorf("Validate expect error: %s, actual: %s", test.exception, err) 71 | } 72 | } 73 | } 74 | 75 | func TestCall(t *testing.T) { 76 | tests := []struct { 77 | methodName string 78 | paramName string 79 | function interface{} 80 | validationFunc func(*functionCache) error 81 | fnParameter interface{} 82 | result interface{} 83 | exception error 84 | }{ 85 | { // A valid function and parameters 86 | "TestCall", "test1", 87 | func(i int) int { return i * 3 }, 88 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(int))), 89 | 3, 90 | 9, 91 | nil, 92 | }, 93 | { // Returns error when the required type doesn't match with the specification 94 | "TestCall", "test2", 95 | func(i int) int { return i * 3 }, 96 | simpleParamValidator(newElemTypeSlice(new(int)), newElemTypeSlice(new(int))), 97 | "not a int", 98 | 9, 99 | errors.New("reflect: Call using string as type int"), 100 | }, 101 | { // A valid function and parameters 102 | "TestCall", "test3", 103 | func(i int) {}, 104 | simpleParamValidator(newElemTypeSlice(new(genericType)), []reflect.Type{}), 105 | 3, 106 | nil, 107 | nil, 108 | }, 109 | } 110 | 111 | for _, test := range tests { 112 | func() { 113 | defer func() { 114 | r := recover() 115 | if !(r == test.exception || r == test.exception.Error()) { 116 | t.Errorf("expect error: nil, actual: %s", r) 117 | } 118 | }() 119 | dynaFunc, err := newGenericFunc(test.methodName, test.paramName, test.function, test.validationFunc) 120 | if err != nil { 121 | t.Errorf("expect error: nil, actual: %s", err) 122 | } 123 | result := dynaFunc.Call(test.fnParameter) 124 | 125 | if result != nil && result != test.result { 126 | t.Errorf("expect result: %d, actual: %d", test.result, result) 127 | } 128 | }() 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ahmetb/go-linq/v3 2 | 3 | go 1.11 4 | -------------------------------------------------------------------------------- /groupby.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Group is a type that is used to store the result of GroupBy method. 4 | type Group struct { 5 | Key interface{} 6 | Group []interface{} 7 | } 8 | 9 | // GroupBy method groups the elements of a collection according to a specified 10 | // key selector function and projects the elements for each group by using a 11 | // specified function. 12 | func (q Query) GroupBy(keySelector func(interface{}) interface{}, 13 | elementSelector func(interface{}) interface{}) Query { 14 | return Query{ 15 | func() Iterator { 16 | next := q.Iterate() 17 | set := make(map[interface{}][]interface{}) 18 | 19 | for item, ok := next(); ok; item, ok = next() { 20 | key := keySelector(item) 21 | set[key] = append(set[key], elementSelector(item)) 22 | } 23 | 24 | len := len(set) 25 | idx := 0 26 | groups := make([]Group, len) 27 | for k, v := range set { 28 | groups[idx] = Group{k, v} 29 | idx++ 30 | } 31 | 32 | index := 0 33 | 34 | return func() (item interface{}, ok bool) { 35 | ok = index < len 36 | if ok { 37 | item = groups[index] 38 | index++ 39 | } 40 | 41 | return 42 | } 43 | }, 44 | } 45 | } 46 | 47 | // GroupByT is the typed version of GroupBy. 48 | // 49 | // - keySelectorFn is of type "func(TSource) TKey" 50 | // - elementSelectorFn is of type "func(TSource) TElement" 51 | // 52 | // NOTE: GroupBy has better performance than GroupByT. 53 | func (q Query) GroupByT(keySelectorFn interface{}, 54 | elementSelectorFn interface{}) Query { 55 | keySelectorGenericFunc, err := newGenericFunc( 56 | "GroupByT", "keySelectorFn", keySelectorFn, 57 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 58 | ) 59 | if err != nil { 60 | panic(err) 61 | } 62 | 63 | keySelectorFunc := func(item interface{}) interface{} { 64 | return keySelectorGenericFunc.Call(item) 65 | } 66 | 67 | elementSelectorGenericFunc, err := newGenericFunc( 68 | "GroupByT", "elementSelectorFn", elementSelectorFn, 69 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 70 | ) 71 | if err != nil { 72 | panic(err) 73 | } 74 | 75 | elementSelectorFunc := func(item interface{}) interface{} { 76 | return elementSelectorGenericFunc.Call(item) 77 | 78 | } 79 | 80 | return q.GroupBy(keySelectorFunc, elementSelectorFunc) 81 | } 82 | -------------------------------------------------------------------------------- /groupby_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestGroupBy(t *testing.T) { 9 | input := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} 10 | wantEven := []interface{}{2, 4, 6, 8} 11 | wantOdd := []interface{}{1, 3, 5, 7, 9} 12 | 13 | q := From(input).GroupBy( 14 | func(i interface{}) interface{} { return i.(int) % 2 }, 15 | func(i interface{}) interface{} { return i.(int) }, 16 | ) 17 | 18 | next := q.Iterate() 19 | eq := true 20 | for item, ok := next(); ok; item, ok = next() { 21 | group := item.(Group) 22 | switch group.Key.(int) { 23 | case 0: 24 | if !reflect.DeepEqual(group.Group, wantEven) { 25 | eq = false 26 | } 27 | case 1: 28 | if !reflect.DeepEqual(group.Group, wantOdd) { 29 | eq = false 30 | } 31 | default: 32 | eq = false 33 | } 34 | } 35 | 36 | if !eq { 37 | t.Errorf("From(%v).GroupBy()=%v", input, toSlice(q)) 38 | } 39 | } 40 | 41 | func TestGroupByT_PanicWhenKeySelectorFnIsInvalid(t *testing.T) { 42 | mustPanicWithError(t, "GroupByT: parameter [keySelectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)bool'", func() { 43 | var r []int 44 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).GroupByT( 45 | func(i, j int) bool { return true }, 46 | func(i int) int { return i }, 47 | ).ToSlice(&r) 48 | }) 49 | } 50 | 51 | func TestGroupByT_PanicWhenElementSelectorFnIsInvalid(t *testing.T) { 52 | mustPanicWithError(t, "GroupByT: parameter [elementSelectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 53 | var r []int 54 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).GroupByT( 55 | func(i int) bool { return true }, 56 | func(i, j int) int { return i }, 57 | ).ToSlice(&r) 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /groupjoin.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "reflect" 4 | 5 | // GroupJoin correlates the elements of two collections based on key equality, 6 | // and groups the results. 7 | // 8 | // This method produces hierarchical results, which means that elements from 9 | // outer query are paired with collections of matching elements from inner. 10 | // GroupJoin enables you to base your results on a whole set of matches for each 11 | // element of outer query. 12 | // 13 | // The resultSelector function is called only one time for each outer element 14 | // together with a collection of all the inner elements that match the outer 15 | // element. This differs from the Join method, in which the result selector 16 | // function is invoked on pairs that contain one element from outer and one 17 | // element from inner. 18 | // 19 | // GroupJoin preserves the order of the elements of outer, and for each element 20 | // of outer, the order of the matching elements from inner. 21 | func (q Query) GroupJoin(inner Query, 22 | outerKeySelector func(interface{}) interface{}, 23 | innerKeySelector func(interface{}) interface{}, 24 | resultSelector func(outer interface{}, inners []interface{}) interface{}) Query { 25 | 26 | return Query{ 27 | Iterate: func() Iterator { 28 | outernext := q.Iterate() 29 | innernext := inner.Iterate() 30 | 31 | innerLookup := make(map[interface{}][]interface{}) 32 | for innerItem, ok := innernext(); ok; innerItem, ok = innernext() { 33 | innerKey := innerKeySelector(innerItem) 34 | innerLookup[innerKey] = append(innerLookup[innerKey], innerItem) 35 | } 36 | 37 | return func() (item interface{}, ok bool) { 38 | if item, ok = outernext(); !ok { 39 | return 40 | } 41 | 42 | if group, has := innerLookup[outerKeySelector(item)]; !has { 43 | item = resultSelector(item, []interface{}{}) 44 | } else { 45 | item = resultSelector(item, group) 46 | } 47 | 48 | return 49 | } 50 | }, 51 | } 52 | } 53 | 54 | // GroupJoinT is the typed version of GroupJoin. 55 | // 56 | // - inner: The query to join to the outer query. 57 | // - outerKeySelectorFn is of type "func(TOuter) TKey" 58 | // - innerKeySelectorFn is of type "func(TInner) TKey" 59 | // - resultSelectorFn: is of type "func(TOuter, inners []TInner) TResult" 60 | // 61 | // NOTE: GroupJoin has better performance than GroupJoinT. 62 | func (q Query) GroupJoinT(inner Query, 63 | outerKeySelectorFn interface{}, 64 | innerKeySelectorFn interface{}, 65 | resultSelectorFn interface{}) Query { 66 | outerKeySelectorGenericFunc, err := newGenericFunc( 67 | "GroupJoinT", "outerKeySelectorFn", outerKeySelectorFn, 68 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 69 | ) 70 | if err != nil { 71 | panic(err) 72 | } 73 | 74 | outerKeySelectorFunc := func(item interface{}) interface{} { 75 | return outerKeySelectorGenericFunc.Call(item) 76 | } 77 | 78 | innerKeySelectorFuncGenericFunc, err := newGenericFunc( 79 | "GroupJoinT", "innerKeySelectorFn", innerKeySelectorFn, 80 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 81 | ) 82 | if err != nil { 83 | panic(err) 84 | } 85 | 86 | innerKeySelectorFunc := func(item interface{}) interface{} { 87 | return innerKeySelectorFuncGenericFunc.Call(item) 88 | } 89 | 90 | resultSelectorGenericFunc, err := newGenericFunc( 91 | "GroupJoinT", "resultSelectorFn", resultSelectorFn, 92 | simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), 93 | ) 94 | if err != nil { 95 | panic(err) 96 | } 97 | 98 | resultSelectorFunc := func(outer interface{}, inners []interface{}) interface{} { 99 | innerSliceType := reflect.MakeSlice(resultSelectorGenericFunc.Cache.TypesIn[1], 0, 0) 100 | innersSlicePointer := reflect.New(innerSliceType.Type()) 101 | From(inners).ToSlice(innersSlicePointer.Interface()) 102 | innersTyped := reflect.Indirect(innersSlicePointer).Interface() 103 | return resultSelectorGenericFunc.Call(outer, innersTyped) 104 | } 105 | 106 | return q.GroupJoin(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc) 107 | } 108 | -------------------------------------------------------------------------------- /groupjoin_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestGroupJoin(t *testing.T) { 6 | outer := []int{0, 1, 2} 7 | inner := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} 8 | want := []interface{}{ 9 | KeyValue{0, 4}, 10 | KeyValue{1, 5}, 11 | KeyValue{2, 0}, 12 | } 13 | 14 | q := From(outer).GroupJoin( 15 | From(inner), 16 | func(i interface{}) interface{} { return i }, 17 | func(i interface{}) interface{} { return i.(int) % 2 }, 18 | func(outer interface{}, inners []interface{}) interface{} { 19 | return KeyValue{outer, len(inners)} 20 | }) 21 | 22 | if !validateQuery(q, want) { 23 | t.Errorf("From().GroupJoin()=%v expected %v", toSlice(q), want) 24 | } 25 | } 26 | 27 | func TestGroupJoinT_PanicWhenOuterKeySelectorFnIsInvalid(t *testing.T) { 28 | mustPanicWithError(t, "GroupJoinT: parameter [outerKeySelectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 29 | From([]int{0, 1, 2}).GroupJoinT( 30 | From([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}), 31 | func(i, j int) int { return i }, 32 | func(i int) int { return i % 2 }, 33 | func(outer int, inners []int) KeyValue { return KeyValue{outer, len(inners)} }, 34 | ) 35 | }) 36 | } 37 | 38 | func TestGroupJoinT_PanicWhenInnerKeySelectorFnIsInvalid(t *testing.T) { 39 | mustPanicWithError(t, "GroupJoinT: parameter [innerKeySelectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 40 | From([]int{0, 1, 2}).GroupJoinT( 41 | From([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}), 42 | func(i int) int { return i }, 43 | func(i, j int) int { return i % 2 }, 44 | func(outer int, inners []int) KeyValue { return KeyValue{outer, len(inners)} }, 45 | ) 46 | }) 47 | } 48 | 49 | func TestGroupJoinT_PanicWhenResultSelectorFnIsInvalid(t *testing.T) { 50 | mustPanicWithError(t, "GroupJoinT: parameter [resultSelectorFn] has a invalid function signature. Expected: 'func(T,T)T', actual: 'func(int,int,[]int)linq.KeyValue'", func() { 51 | From([]int{0, 1, 2}).GroupJoinT( 52 | From([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}), 53 | func(i int) int { return i }, 54 | func(i int) int { return i % 2 }, 55 | func(outer, j int, inners []int) KeyValue { return KeyValue{outer, len(inners)} }, 56 | ) 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /index.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // IndexOf searches for an element that matches the conditions defined by a specified predicate 4 | // and returns the zero-based index of the first occurrence within the collection. This method 5 | // returns -1 if an item that matches the conditions is not found. 6 | func (q Query) IndexOf(predicate func(interface{}) bool) int { 7 | index := 0 8 | next := q.Iterate() 9 | 10 | for item, ok := next(); ok; item, ok = next() { 11 | if predicate(item) { 12 | return index 13 | } 14 | index++ 15 | } 16 | 17 | return -1 18 | } 19 | 20 | // IndexOfT is the typed version of IndexOf. 21 | // 22 | // - predicateFn is of type "func(int,TSource)bool" 23 | // 24 | // NOTE: IndexOf has better performance than IndexOfT. 25 | func (q Query) IndexOfT(predicateFn interface{}) int { 26 | 27 | predicateGenericFunc, err := newGenericFunc( 28 | "IndexOfT", "predicateFn", predicateFn, 29 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 30 | ) 31 | if err != nil { 32 | panic(err) 33 | } 34 | 35 | predicateFunc := func(item interface{}) bool { 36 | return predicateGenericFunc.Call(item).(bool) 37 | } 38 | 39 | return q.IndexOf(predicateFunc) 40 | } 41 | -------------------------------------------------------------------------------- /index_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIndexOf(t *testing.T) { 8 | tests := []struct { 9 | input interface{} 10 | predicate func(interface{}) bool 11 | expected int 12 | }{ 13 | { 14 | input: [9]int{1, 2, 3, 4, 5, 6, 7, 8, 9}, 15 | predicate: func(i interface{}) bool { 16 | return i.(int) == 3 17 | }, 18 | expected: 2, 19 | }, 20 | { 21 | input: "sstr", 22 | predicate: func(i interface{}) bool { 23 | return i.(rune) == 'r' 24 | }, 25 | expected: 3, 26 | }, 27 | { 28 | input: "gadsgsadgsda", 29 | predicate: func(i interface{}) bool { 30 | return i.(rune) == 'z' 31 | }, 32 | expected: -1, 33 | }, 34 | } 35 | 36 | for _, test := range tests { 37 | index := From(test.input).IndexOf(test.predicate) 38 | if index != test.expected { 39 | t.Errorf("From(%v).IndexOf() expected %v received %v", test.input, test.expected, index) 40 | } 41 | 42 | index = From(test.input).IndexOfT(test.predicate) 43 | if index != test.expected { 44 | t.Errorf("From(%v).IndexOfT() expected %v received %v", test.input, test.expected, index) 45 | } 46 | } 47 | } 48 | 49 | func TestIndexOfT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 50 | mustPanicWithError(t, "IndexOfT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int)int'", func() { 51 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).IndexOfT(func(item int) int { return item + 2 }) 52 | }) 53 | } 54 | -------------------------------------------------------------------------------- /intersect.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Intersect produces the set intersection of the source collection and the 4 | // provided input collection. The intersection of two sets A and B is defined as 5 | // the set that contains all the elements of A that also appear in B, but no 6 | // other elements. 7 | func (q Query) Intersect(q2 Query) Query { 8 | return Query{ 9 | Iterate: func() Iterator { 10 | next := q.Iterate() 11 | next2 := q2.Iterate() 12 | 13 | set := make(map[interface{}]bool) 14 | for item, ok := next2(); ok; item, ok = next2() { 15 | set[item] = true 16 | } 17 | 18 | return func() (item interface{}, ok bool) { 19 | for item, ok = next(); ok; item, ok = next() { 20 | if _, has := set[item]; has { 21 | delete(set, item) 22 | return 23 | } 24 | } 25 | 26 | return 27 | } 28 | }, 29 | } 30 | } 31 | 32 | // IntersectBy produces the set intersection of the source collection and the 33 | // provided input collection. The intersection of two sets A and B is defined as 34 | // the set that contains all the elements of A that also appear in B, but no 35 | // other elements. 36 | // 37 | // IntersectBy invokes a transform function on each element of both collections. 38 | func (q Query) IntersectBy(q2 Query, 39 | selector func(interface{}) interface{}) Query { 40 | 41 | return Query{ 42 | Iterate: func() Iterator { 43 | next := q.Iterate() 44 | next2 := q2.Iterate() 45 | 46 | set := make(map[interface{}]bool) 47 | for item, ok := next2(); ok; item, ok = next2() { 48 | s := selector(item) 49 | set[s] = true 50 | } 51 | 52 | return func() (item interface{}, ok bool) { 53 | for item, ok = next(); ok; item, ok = next() { 54 | s := selector(item) 55 | if _, has := set[s]; has { 56 | delete(set, s) 57 | return 58 | } 59 | } 60 | 61 | return 62 | } 63 | }, 64 | } 65 | } 66 | 67 | // IntersectByT is the typed version of IntersectBy. 68 | // 69 | // - selectorFn is of type "func(TSource) TSource" 70 | // 71 | // NOTE: IntersectBy has better performance than IntersectByT. 72 | func (q Query) IntersectByT(q2 Query, 73 | selectorFn interface{}) Query { 74 | selectorGenericFunc, err := newGenericFunc( 75 | "IntersectByT", "selectorFn", selectorFn, 76 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 77 | ) 78 | if err != nil { 79 | panic(err) 80 | } 81 | 82 | selectorFunc := func(item interface{}) interface{} { 83 | return selectorGenericFunc.Call(item) 84 | } 85 | 86 | return q.IntersectBy(q2, selectorFunc) 87 | } 88 | -------------------------------------------------------------------------------- /intersect_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestIntersect(t *testing.T) { 6 | input1 := []int{1, 2, 3} 7 | input2 := []int{1, 4, 7, 9, 12, 3} 8 | want := []interface{}{1, 3} 9 | 10 | if q := From(input1).Intersect(From(input2)); !validateQuery(q, want) { 11 | t.Errorf("From(%v).Intersect(%v)=%v expected %v", input1, input2, toSlice(q), want) 12 | } 13 | } 14 | 15 | func TestIntersectBy(t *testing.T) { 16 | input1 := []int{5, 7, 8} 17 | input2 := []int{1, 4, 7, 9, 12, 3} 18 | want := []interface{}{5, 8} 19 | 20 | if q := From(input1).IntersectBy(From(input2), func(i interface{}) interface{} { 21 | return i.(int) % 2 22 | }); !validateQuery(q, want) { 23 | t.Errorf("From(%v).IntersectBy(%v)=%v expected %v", input1, input2, toSlice(q), want) 24 | } 25 | } 26 | 27 | func TestIntersectByT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 28 | mustPanicWithError(t, "IntersectByT: parameter [selectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 29 | From([]int{5, 7, 8}).IntersectByT(From([]int{1, 4, 7, 9, 12, 3}), func(i, x int) int { 30 | return i % 2 31 | }) 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /join.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Join correlates the elements of two collection based on matching keys. 4 | // 5 | // A join refers to the operation of correlating the elements of two sources of 6 | // information based on a common key. Join brings the two information sources 7 | // and the keys by which they are matched together in one method call. This 8 | // differs from the use of SelectMany, which requires more than one method call 9 | // to perform the same operation. 10 | // 11 | // Join preserves the order of the elements of outer collection, and for each of 12 | // these elements, the order of the matching elements of inner. 13 | func (q Query) Join(inner Query, 14 | outerKeySelector func(interface{}) interface{}, 15 | innerKeySelector func(interface{}) interface{}, 16 | resultSelector func(outer interface{}, inner interface{}) interface{}) Query { 17 | 18 | return Query{ 19 | Iterate: func() Iterator { 20 | outernext := q.Iterate() 21 | innernext := inner.Iterate() 22 | 23 | innerLookup := make(map[interface{}][]interface{}) 24 | for innerItem, ok := innernext(); ok; innerItem, ok = innernext() { 25 | innerKey := innerKeySelector(innerItem) 26 | innerLookup[innerKey] = append(innerLookup[innerKey], innerItem) 27 | } 28 | 29 | var outerItem interface{} 30 | var innerGroup []interface{} 31 | innerLen, innerIndex := 0, 0 32 | 33 | return func() (item interface{}, ok bool) { 34 | if innerIndex >= innerLen { 35 | has := false 36 | for !has { 37 | outerItem, ok = outernext() 38 | if !ok { 39 | return 40 | } 41 | 42 | innerGroup, has = innerLookup[outerKeySelector(outerItem)] 43 | innerLen = len(innerGroup) 44 | innerIndex = 0 45 | } 46 | } 47 | 48 | item = resultSelector(outerItem, innerGroup[innerIndex]) 49 | innerIndex++ 50 | return item, true 51 | } 52 | }, 53 | } 54 | } 55 | 56 | // JoinT is the typed version of Join. 57 | // 58 | // - outerKeySelectorFn is of type "func(TOuter) TKey" 59 | // - innerKeySelectorFn is of type "func(TInner) TKey" 60 | // - resultSelectorFn is of type "func(TOuter,TInner) TResult" 61 | // 62 | // NOTE: Join has better performance than JoinT. 63 | func (q Query) JoinT(inner Query, 64 | outerKeySelectorFn interface{}, 65 | innerKeySelectorFn interface{}, 66 | resultSelectorFn interface{}) Query { 67 | outerKeySelectorGenericFunc, err := newGenericFunc( 68 | "JoinT", "outerKeySelectorFn", outerKeySelectorFn, 69 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 70 | ) 71 | if err != nil { 72 | panic(err) 73 | } 74 | 75 | outerKeySelectorFunc := func(item interface{}) interface{} { 76 | return outerKeySelectorGenericFunc.Call(item) 77 | } 78 | 79 | innerKeySelectorFuncGenericFunc, err := newGenericFunc( 80 | "JoinT", "innerKeySelectorFn", 81 | innerKeySelectorFn, 82 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 83 | ) 84 | if err != nil { 85 | panic(err) 86 | } 87 | 88 | innerKeySelectorFunc := func(item interface{}) interface{} { 89 | return innerKeySelectorFuncGenericFunc.Call(item) 90 | } 91 | 92 | resultSelectorGenericFunc, err := newGenericFunc( 93 | "JoinT", "resultSelectorFn", resultSelectorFn, 94 | simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), 95 | ) 96 | if err != nil { 97 | panic(err) 98 | } 99 | 100 | resultSelectorFunc := func(outer interface{}, inner interface{}) interface{} { 101 | return resultSelectorGenericFunc.Call(outer, inner) 102 | } 103 | 104 | return q.Join(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc) 105 | } 106 | -------------------------------------------------------------------------------- /join_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestJoin(t *testing.T) { 6 | outer := []int{0, 1, 2, 3, 4, 5, 8} 7 | inner := []int{1, 2, 1, 4, 7, 6, 7, 2} 8 | want := []interface{}{ 9 | KeyValue{1, 1}, 10 | KeyValue{1, 1}, 11 | KeyValue{2, 2}, 12 | KeyValue{2, 2}, 13 | KeyValue{4, 4}, 14 | } 15 | 16 | q := From(outer).Join( 17 | From(inner), 18 | func(i interface{}) interface{} { return i }, 19 | func(i interface{}) interface{} { return i }, 20 | func(outer interface{}, inner interface{}) interface{} { 21 | return KeyValue{outer, inner} 22 | }) 23 | 24 | if !validateQuery(q, want) { 25 | t.Errorf("From().Join()=%v expected %v", toSlice(q), want) 26 | } 27 | } 28 | 29 | func TestJoinT_PanicWhenOuterKeySelectorFnIsInvalid(t *testing.T) { 30 | mustPanicWithError(t, "JoinT: parameter [outerKeySelectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 31 | From([]int{0, 1, 2}).JoinT( 32 | From([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}), 33 | func(i, j int) int { return i }, 34 | func(i int) int { return i % 2 }, 35 | func(outer int, inner int) KeyValue { return KeyValue{outer, inner} }, 36 | ) 37 | }) 38 | } 39 | 40 | func TestJoinT_PanicWhenInnerKeySelectorFnIsInvalid(t *testing.T) { 41 | mustPanicWithError(t, "JoinT: parameter [innerKeySelectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 42 | From([]int{0, 1, 2}).JoinT( 43 | From([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}), 44 | func(i int) int { return i }, 45 | func(i, j int) int { return i % 2 }, 46 | func(outer int, inners []int) KeyValue { return KeyValue{outer, len(inners)} }, 47 | ) 48 | }) 49 | } 50 | 51 | func TestJoinT_PanicWhenResultSelectorFnIsInvalid(t *testing.T) { 52 | mustPanicWithError(t, "JoinT: parameter [resultSelectorFn] has a invalid function signature. Expected: 'func(T,T)T', actual: 'func(int,int,int)linq.KeyValue'", func() { 53 | From([]int{0, 1, 2}).JoinT( 54 | From([]int{1, 2, 3, 4, 5, 6, 7, 8, 9}), 55 | func(i int) int { return i }, 56 | func(i int) int { return i % 2 }, 57 | func(outer int, inner, j int) KeyValue { return KeyValue{outer, inner} }, 58 | ) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /orderby.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "sort" 4 | 5 | type order struct { 6 | selector func(interface{}) interface{} 7 | compare comparer 8 | desc bool 9 | } 10 | 11 | // OrderedQuery is the type returned from OrderBy, OrderByDescending ThenBy and 12 | // ThenByDescending functions. 13 | type OrderedQuery struct { 14 | Query 15 | original Query 16 | orders []order 17 | } 18 | 19 | // OrderBy sorts the elements of a collection in ascending order. Elements are 20 | // sorted according to a key. 21 | func (q Query) OrderBy(selector func(interface{}) interface{}) OrderedQuery { 22 | return OrderedQuery{ 23 | orders: []order{{selector: selector}}, 24 | original: q, 25 | Query: Query{ 26 | Iterate: func() Iterator { 27 | items := q.sort([]order{{selector: selector}}) 28 | len := len(items) 29 | index := 0 30 | 31 | return func() (item interface{}, ok bool) { 32 | ok = index < len 33 | if ok { 34 | item = items[index] 35 | index++ 36 | } 37 | 38 | return 39 | } 40 | }, 41 | }, 42 | } 43 | } 44 | 45 | // OrderByT is the typed version of OrderBy. 46 | // 47 | // - selectorFn is of type "func(TSource) TKey" 48 | // 49 | // NOTE: OrderBy has better performance than OrderByT. 50 | func (q Query) OrderByT(selectorFn interface{}) OrderedQuery { 51 | selectorGenericFunc, err := newGenericFunc( 52 | "OrderByT", "selectorFn", selectorFn, 53 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 54 | ) 55 | if err != nil { 56 | panic(err) 57 | } 58 | 59 | selectorFunc := func(item interface{}) interface{} { 60 | return selectorGenericFunc.Call(item) 61 | } 62 | 63 | return q.OrderBy(selectorFunc) 64 | } 65 | 66 | // OrderByDescending sorts the elements of a collection in descending order. 67 | // Elements are sorted according to a key. 68 | func (q Query) OrderByDescending(selector func(interface{}) interface{}) OrderedQuery { 69 | return OrderedQuery{ 70 | orders: []order{{selector: selector, desc: true}}, 71 | original: q, 72 | Query: Query{ 73 | Iterate: func() Iterator { 74 | items := q.sort([]order{{selector: selector, desc: true}}) 75 | len := len(items) 76 | index := 0 77 | 78 | return func() (item interface{}, ok bool) { 79 | ok = index < len 80 | if ok { 81 | item = items[index] 82 | index++ 83 | } 84 | 85 | return 86 | } 87 | }, 88 | }, 89 | } 90 | } 91 | 92 | // OrderByDescendingT is the typed version of OrderByDescending. 93 | // - selectorFn is of type "func(TSource) TKey" 94 | // NOTE: OrderByDescending has better performance than OrderByDescendingT. 95 | func (q Query) OrderByDescendingT(selectorFn interface{}) OrderedQuery { 96 | selectorGenericFunc, err := newGenericFunc( 97 | "OrderByDescendingT", "selectorFn", selectorFn, 98 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 99 | ) 100 | if err != nil { 101 | panic(err) 102 | } 103 | 104 | selectorFunc := func(item interface{}) interface{} { 105 | return selectorGenericFunc.Call(item) 106 | } 107 | 108 | return q.OrderByDescending(selectorFunc) 109 | } 110 | 111 | // ThenBy performs a subsequent ordering of the elements in a collection in 112 | // ascending order. This method enables you to specify multiple sort criteria by 113 | // applying any number of ThenBy or ThenByDescending methods. 114 | func (oq OrderedQuery) ThenBy( 115 | selector func(interface{}) interface{}) OrderedQuery { 116 | return OrderedQuery{ 117 | orders: append(oq.orders, order{selector: selector}), 118 | original: oq.original, 119 | Query: Query{ 120 | Iterate: func() Iterator { 121 | items := oq.original.sort(append(oq.orders, order{selector: selector})) 122 | len := len(items) 123 | index := 0 124 | 125 | return func() (item interface{}, ok bool) { 126 | ok = index < len 127 | if ok { 128 | item = items[index] 129 | index++ 130 | } 131 | 132 | return 133 | } 134 | }, 135 | }, 136 | } 137 | } 138 | 139 | // ThenByT is the typed version of ThenBy. 140 | // - selectorFn is of type "func(TSource) TKey" 141 | // NOTE: ThenBy has better performance than ThenByT. 142 | func (oq OrderedQuery) ThenByT(selectorFn interface{}) OrderedQuery { 143 | selectorGenericFunc, err := newGenericFunc( 144 | "ThenByT", "selectorFn", selectorFn, 145 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 146 | ) 147 | if err != nil { 148 | panic(err) 149 | } 150 | 151 | selectorFunc := func(item interface{}) interface{} { 152 | return selectorGenericFunc.Call(item) 153 | } 154 | 155 | return oq.ThenBy(selectorFunc) 156 | } 157 | 158 | // ThenByDescending performs a subsequent ordering of the elements in a 159 | // collection in descending order. This method enables you to specify multiple 160 | // sort criteria by applying any number of ThenBy or ThenByDescending methods. 161 | func (oq OrderedQuery) ThenByDescending(selector func(interface{}) interface{}) OrderedQuery { 162 | return OrderedQuery{ 163 | orders: append(oq.orders, order{selector: selector, desc: true}), 164 | original: oq.original, 165 | Query: Query{ 166 | Iterate: func() Iterator { 167 | items := oq.original.sort(append(oq.orders, order{selector: selector, desc: true})) 168 | len := len(items) 169 | index := 0 170 | 171 | return func() (item interface{}, ok bool) { 172 | ok = index < len 173 | if ok { 174 | item = items[index] 175 | index++ 176 | } 177 | 178 | return 179 | } 180 | }, 181 | }, 182 | } 183 | } 184 | 185 | // ThenByDescendingT is the typed version of ThenByDescending. 186 | // - selectorFn is of type "func(TSource) TKey" 187 | // NOTE: ThenByDescending has better performance than ThenByDescendingT. 188 | func (oq OrderedQuery) ThenByDescendingT(selectorFn interface{}) OrderedQuery { 189 | selectorFunc, ok := selectorFn.(func(interface{}) interface{}) 190 | if !ok { 191 | selectorGenericFunc, err := newGenericFunc( 192 | "ThenByDescending", "selectorFn", selectorFn, 193 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 194 | ) 195 | if err != nil { 196 | panic(err) 197 | } 198 | 199 | selectorFunc = func(item interface{}) interface{} { 200 | return selectorGenericFunc.Call(item) 201 | } 202 | } 203 | return oq.ThenByDescending(selectorFunc) 204 | } 205 | 206 | // Sort returns a new query by sorting elements with provided less function in 207 | // ascending order. The comparer function should return true if the parameter i 208 | // is less than j. While this method is uglier than chaining OrderBy, 209 | // OrderByDescending, ThenBy and ThenByDescending methods, it's performance is 210 | // much better. 211 | func (q Query) Sort(less func(i, j interface{}) bool) Query { 212 | return Query{ 213 | Iterate: func() Iterator { 214 | items := q.lessSort(less) 215 | len := len(items) 216 | index := 0 217 | 218 | return func() (item interface{}, ok bool) { 219 | ok = index < len 220 | if ok { 221 | item = items[index] 222 | index++ 223 | } 224 | 225 | return 226 | } 227 | }, 228 | } 229 | } 230 | 231 | // SortT is the typed version of Sort. 232 | // - lessFn is of type "func(TSource,TSource) bool" 233 | // NOTE: Sort has better performance than SortT. 234 | func (q Query) SortT(lessFn interface{}) Query { 235 | lessGenericFunc, err := newGenericFunc( 236 | "SortT", "lessFn", lessFn, 237 | simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(bool))), 238 | ) 239 | if err != nil { 240 | panic(err) 241 | } 242 | 243 | lessFunc := func(i, j interface{}) bool { 244 | return lessGenericFunc.Call(i, j).(bool) 245 | } 246 | 247 | return q.Sort(lessFunc) 248 | } 249 | 250 | type sorter struct { 251 | items []interface{} 252 | less func(i, j interface{}) bool 253 | } 254 | 255 | func (s sorter) Len() int { 256 | return len(s.items) 257 | } 258 | 259 | func (s sorter) Swap(i, j int) { 260 | s.items[i], s.items[j] = s.items[j], s.items[i] 261 | } 262 | 263 | func (s sorter) Less(i, j int) bool { 264 | return s.less(s.items[i], s.items[j]) 265 | } 266 | 267 | func (q Query) sort(orders []order) (r []interface{}) { 268 | next := q.Iterate() 269 | for item, ok := next(); ok; item, ok = next() { 270 | r = append(r, item) 271 | } 272 | 273 | if len(r) == 0 { 274 | return 275 | } 276 | 277 | for i, j := range orders { 278 | orders[i].compare = getComparer(j.selector(r[0])) 279 | } 280 | 281 | s := sorter{ 282 | items: r, 283 | less: func(i, j interface{}) bool { 284 | for _, order := range orders { 285 | x, y := order.selector(i), order.selector(j) 286 | switch order.compare(x, y) { 287 | case 0: 288 | continue 289 | case -1: 290 | return !order.desc 291 | default: 292 | return order.desc 293 | } 294 | } 295 | 296 | return false 297 | }} 298 | 299 | sort.Sort(s) 300 | return 301 | } 302 | 303 | func (q Query) lessSort(less func(i, j interface{}) bool) (r []interface{}) { 304 | next := q.Iterate() 305 | for item, ok := next(); ok; item, ok = next() { 306 | r = append(r, item) 307 | } 308 | 309 | s := sorter{items: r, less: less} 310 | 311 | sort.Sort(s) 312 | return 313 | } 314 | -------------------------------------------------------------------------------- /orderby_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestEmpty(t *testing.T) { 6 | q := From([]string{}).OrderBy(func(in interface{}) interface{} { 7 | return 0 8 | }) 9 | 10 | _, ok := q.Iterate()() 11 | if ok { 12 | t.Errorf("Iterator for empty collection must return ok=false") 13 | } 14 | } 15 | 16 | func TestOrderBy(t *testing.T) { 17 | slice := make([]foo, 100) 18 | 19 | for i := len(slice) - 1; i >= 0; i-- { 20 | slice[i].f1 = i 21 | } 22 | 23 | q := From(slice).OrderBy(func(i interface{}) interface{} { 24 | return i.(foo).f1 25 | }) 26 | 27 | j := 0 28 | next := q.Iterate() 29 | for item, ok := next(); ok; item, ok = next() { 30 | if item.(foo).f1 != j { 31 | t.Errorf("OrderBy()[%v]=%v expected %v", j, item, foo{f1: j}) 32 | } 33 | 34 | j++ 35 | } 36 | } 37 | 38 | func TestOrderByT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 39 | mustPanicWithError(t, "OrderByT: parameter [selectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 40 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).OrderByT(func(item, j int) int { return item + 2 }) 41 | }) 42 | } 43 | 44 | func TestOrderByDescending(t *testing.T) { 45 | slice := make([]foo, 100) 46 | 47 | for i := 0; i < len(slice); i++ { 48 | slice[i].f1 = i 49 | } 50 | 51 | q := From(slice).OrderByDescending(func(i interface{}) interface{} { 52 | return i.(foo).f1 53 | }) 54 | 55 | j := len(slice) - 1 56 | next := q.Iterate() 57 | for item, ok := next(); ok; item, ok = next() { 58 | if item.(foo).f1 != j { 59 | t.Errorf("OrderByDescending()[%v]=%v expected %v", j, item, foo{f1: j}) 60 | } 61 | 62 | j-- 63 | } 64 | } 65 | 66 | func TestOrderByDescendingT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 67 | mustPanicWithError(t, "OrderByDescendingT: parameter [selectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 68 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).OrderByDescendingT(func(item, j int) int { return item + 2 }) 69 | }) 70 | } 71 | 72 | func TestThenBy(t *testing.T) { 73 | slice := make([]foo, 1000) 74 | 75 | for i := len(slice) - 1; i >= 0; i-- { 76 | slice[i].f1 = i 77 | slice[i].f2 = i%2 == 0 78 | } 79 | 80 | q := From(slice).OrderBy(func(i interface{}) interface{} { 81 | return i.(foo).f2 82 | }).ThenBy(func(i interface{}) interface{} { 83 | return i.(foo).f1 84 | }) 85 | 86 | next := q.Iterate() 87 | for item, ok := next(); ok; item, ok = next() { 88 | if item.(foo).f2 != (item.(foo).f1%2 == 0) { 89 | t.Errorf("OrderBy().ThenBy()=%v", item) 90 | } 91 | } 92 | } 93 | 94 | func TestThenByT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 95 | mustPanicWithError(t, "ThenByT: parameter [selectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)bool'", func() { 96 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}). 97 | OrderByT(func(item int) int { return item }). 98 | ThenByT(func(item, j int) bool { return true }) 99 | }) 100 | } 101 | 102 | func TestThenByDescending(t *testing.T) { 103 | slice := make([]foo, 1000) 104 | 105 | for i := len(slice) - 1; i >= 0; i-- { 106 | slice[i].f1 = i 107 | slice[i].f2 = i%2 == 0 108 | } 109 | 110 | q := From(slice).OrderBy(func(i interface{}) interface{} { 111 | return i.(foo).f2 112 | }).ThenByDescending(func(i interface{}) interface{} { 113 | return i.(foo).f1 114 | }) 115 | 116 | next := q.Iterate() 117 | for item, ok := next(); ok; item, ok = next() { 118 | if item.(foo).f2 != (item.(foo).f1%2 == 0) { 119 | t.Errorf("OrderBy().ThenByDescending()=%v", item) 120 | } 121 | } 122 | } 123 | 124 | func TestThenByDescendingT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 125 | mustPanicWithError(t, "ThenByDescending: parameter [selectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)bool'", func() { 126 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}). 127 | OrderByT(func(item int) int { return item }). 128 | ThenByDescendingT(func(item, j int) bool { return true }) 129 | }) 130 | } 131 | 132 | func TestSort(t *testing.T) { 133 | slice := make([]foo, 100) 134 | 135 | for i := len(slice) - 1; i >= 0; i-- { 136 | slice[i].f1 = i 137 | } 138 | 139 | q := From(slice).Sort(func(i, j interface{}) bool { 140 | return i.(foo).f1 < j.(foo).f1 141 | }) 142 | 143 | j := 0 144 | next := q.Iterate() 145 | for item, ok := next(); ok; item, ok = next() { 146 | if item.(foo).f1 != j { 147 | t.Errorf("Sort()[%v]=%v expected %v", j, item, foo{f1: j}) 148 | } 149 | 150 | j++ 151 | } 152 | } 153 | 154 | func TestSortT_PanicWhenLessFnIsInvalid(t *testing.T) { 155 | mustPanicWithError(t, "SortT: parameter [lessFn] has a invalid function signature. Expected: 'func(T,T)bool', actual: 'func(int,int)string'", func() { 156 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).SortT(func(i, j int) string { return "" }) 157 | }) 158 | } 159 | -------------------------------------------------------------------------------- /result.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import ( 4 | "math" 5 | "reflect" 6 | ) 7 | 8 | // All determines whether all elements of a collection satisfy a condition. 9 | func (q Query) All(predicate func(interface{}) bool) bool { 10 | next := q.Iterate() 11 | 12 | for item, ok := next(); ok; item, ok = next() { 13 | if !predicate(item) { 14 | return false 15 | } 16 | } 17 | 18 | return true 19 | } 20 | 21 | // AllT is the typed version of All. 22 | // 23 | // - predicateFn is of type "func(TSource) bool" 24 | // 25 | // NOTE: All has better performance than AllT. 26 | func (q Query) AllT(predicateFn interface{}) bool { 27 | 28 | predicateGenericFunc, err := newGenericFunc( 29 | "AllT", "predicateFn", predicateFn, 30 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 31 | ) 32 | if err != nil { 33 | panic(err) 34 | } 35 | predicateFunc := func(item interface{}) bool { 36 | return predicateGenericFunc.Call(item).(bool) 37 | } 38 | 39 | return q.All(predicateFunc) 40 | } 41 | 42 | // Any determines whether any element of a collection exists. 43 | func (q Query) Any() bool { 44 | _, ok := q.Iterate()() 45 | return ok 46 | } 47 | 48 | // AnyWith determines whether any element of a collection satisfies a condition. 49 | func (q Query) AnyWith(predicate func(interface{}) bool) bool { 50 | next := q.Iterate() 51 | 52 | for item, ok := next(); ok; item, ok = next() { 53 | if predicate(item) { 54 | return true 55 | } 56 | } 57 | 58 | return false 59 | } 60 | 61 | // AnyWithT is the typed version of AnyWith. 62 | // 63 | // - predicateFn is of type "func(TSource) bool" 64 | // 65 | // NOTE: AnyWith has better performance than AnyWithT. 66 | func (q Query) AnyWithT(predicateFn interface{}) bool { 67 | 68 | predicateGenericFunc, err := newGenericFunc( 69 | "AnyWithT", "predicateFn", predicateFn, 70 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 71 | ) 72 | if err != nil { 73 | panic(err) 74 | } 75 | 76 | predicateFunc := func(item interface{}) bool { 77 | return predicateGenericFunc.Call(item).(bool) 78 | } 79 | 80 | return q.AnyWith(predicateFunc) 81 | } 82 | 83 | // Average computes the average of a collection of numeric values. 84 | func (q Query) Average() (r float64) { 85 | next := q.Iterate() 86 | item, ok := next() 87 | if !ok { 88 | return math.NaN() 89 | } 90 | 91 | n := 1 92 | switch item.(type) { 93 | case int, int8, int16, int32, int64: 94 | conv := getIntConverter(item) 95 | sum := conv(item) 96 | 97 | for item, ok = next(); ok; item, ok = next() { 98 | sum += conv(item) 99 | n++ 100 | } 101 | 102 | r = float64(sum) 103 | case uint, uint8, uint16, uint32, uint64: 104 | conv := getUIntConverter(item) 105 | sum := conv(item) 106 | 107 | for item, ok = next(); ok; item, ok = next() { 108 | sum += conv(item) 109 | n++ 110 | } 111 | 112 | r = float64(sum) 113 | default: 114 | conv := getFloatConverter(item) 115 | r = conv(item) 116 | 117 | for item, ok = next(); ok; item, ok = next() { 118 | r += conv(item) 119 | n++ 120 | } 121 | } 122 | 123 | return r / float64(n) 124 | } 125 | 126 | // Contains determines whether a collection contains a specified element. 127 | func (q Query) Contains(value interface{}) bool { 128 | next := q.Iterate() 129 | 130 | for item, ok := next(); ok; item, ok = next() { 131 | if item == value { 132 | return true 133 | } 134 | } 135 | 136 | return false 137 | } 138 | 139 | // Count returns the number of elements in a collection. 140 | func (q Query) Count() (r int) { 141 | next := q.Iterate() 142 | 143 | for _, ok := next(); ok; _, ok = next() { 144 | r++ 145 | } 146 | 147 | return 148 | } 149 | 150 | // CountWith returns a number that represents how many elements in the specified 151 | // collection satisfy a condition. 152 | func (q Query) CountWith(predicate func(interface{}) bool) (r int) { 153 | next := q.Iterate() 154 | 155 | for item, ok := next(); ok; item, ok = next() { 156 | if predicate(item) { 157 | r++ 158 | } 159 | } 160 | 161 | return 162 | } 163 | 164 | // CountWithT is the typed version of CountWith. 165 | // 166 | // - predicateFn is of type "func(TSource) bool" 167 | // 168 | // NOTE: CountWith has better performance than CountWithT. 169 | func (q Query) CountWithT(predicateFn interface{}) int { 170 | 171 | predicateGenericFunc, err := newGenericFunc( 172 | "CountWithT", "predicateFn", predicateFn, 173 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 174 | ) 175 | if err != nil { 176 | panic(err) 177 | } 178 | 179 | predicateFunc := func(item interface{}) bool { 180 | return predicateGenericFunc.Call(item).(bool) 181 | } 182 | 183 | return q.CountWith(predicateFunc) 184 | } 185 | 186 | // First returns the first element of a collection. 187 | func (q Query) First() interface{} { 188 | item, _ := q.Iterate()() 189 | return item 190 | } 191 | 192 | // FirstWith returns the first element of a collection that satisfies a 193 | // specified condition. 194 | func (q Query) FirstWith(predicate func(interface{}) bool) interface{} { 195 | next := q.Iterate() 196 | 197 | for item, ok := next(); ok; item, ok = next() { 198 | if predicate(item) { 199 | return item 200 | } 201 | } 202 | 203 | return nil 204 | } 205 | 206 | // FirstWithT is the typed version of FirstWith. 207 | // 208 | // - predicateFn is of type "func(TSource) bool" 209 | // 210 | // NOTE: FirstWith has better performance than FirstWithT. 211 | func (q Query) FirstWithT(predicateFn interface{}) interface{} { 212 | 213 | predicateGenericFunc, err := newGenericFunc( 214 | "FirstWithT", "predicateFn", predicateFn, 215 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 216 | ) 217 | if err != nil { 218 | panic(err) 219 | } 220 | 221 | predicateFunc := func(item interface{}) bool { 222 | return predicateGenericFunc.Call(item).(bool) 223 | } 224 | 225 | return q.FirstWith(predicateFunc) 226 | } 227 | 228 | // ForEach performs the specified action on each element of a collection. 229 | func (q Query) ForEach(action func(interface{})) { 230 | next := q.Iterate() 231 | 232 | for item, ok := next(); ok; item, ok = next() { 233 | action(item) 234 | } 235 | } 236 | 237 | // ForEachT is the typed version of ForEach. 238 | // 239 | // - actionFn is of type "func(TSource)" 240 | // 241 | // NOTE: ForEach has better performance than ForEachT. 242 | func (q Query) ForEachT(actionFn interface{}) { 243 | actionGenericFunc, err := newGenericFunc( 244 | "ForEachT", "actionFn", actionFn, 245 | simpleParamValidator(newElemTypeSlice(new(genericType)), nil), 246 | ) 247 | 248 | if err != nil { 249 | panic(err) 250 | } 251 | 252 | actionFunc := func(item interface{}) { 253 | actionGenericFunc.Call(item) 254 | } 255 | 256 | q.ForEach(actionFunc) 257 | } 258 | 259 | // ForEachIndexed performs the specified action on each element of a collection. 260 | // 261 | // The first argument to action represents the zero-based index of that 262 | // element in the source collection. This can be useful if the elements are in a 263 | // known order and you want to do something with an element at a particular 264 | // index, for example. It can also be useful if you want to retrieve the index 265 | // of one or more elements. The second argument to action represents the 266 | // element to process. 267 | func (q Query) ForEachIndexed(action func(int, interface{})) { 268 | next := q.Iterate() 269 | index := 0 270 | 271 | for item, ok := next(); ok; item, ok = next() { 272 | action(index, item) 273 | index++ 274 | } 275 | } 276 | 277 | // ForEachIndexedT is the typed version of ForEachIndexed. 278 | // 279 | // - actionFn is of type "func(int, TSource)" 280 | // 281 | // NOTE: ForEachIndexed has better performance than ForEachIndexedT. 282 | func (q Query) ForEachIndexedT(actionFn interface{}) { 283 | actionGenericFunc, err := newGenericFunc( 284 | "ForEachIndexedT", "actionFn", actionFn, 285 | simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), nil), 286 | ) 287 | 288 | if err != nil { 289 | panic(err) 290 | } 291 | 292 | actionFunc := func(index int, item interface{}) { 293 | actionGenericFunc.Call(index, item) 294 | } 295 | 296 | q.ForEachIndexed(actionFunc) 297 | } 298 | 299 | // Last returns the last element of a collection. 300 | func (q Query) Last() (r interface{}) { 301 | next := q.Iterate() 302 | 303 | for item, ok := next(); ok; item, ok = next() { 304 | r = item 305 | } 306 | 307 | return 308 | } 309 | 310 | // LastWith returns the last element of a collection that satisfies a specified 311 | // condition. 312 | func (q Query) LastWith(predicate func(interface{}) bool) (r interface{}) { 313 | next := q.Iterate() 314 | 315 | for item, ok := next(); ok; item, ok = next() { 316 | if predicate(item) { 317 | r = item 318 | } 319 | } 320 | 321 | return 322 | } 323 | 324 | // LastWithT is the typed version of LastWith. 325 | // 326 | // - predicateFn is of type "func(TSource) bool" 327 | // 328 | // NOTE: LastWith has better performance than LastWithT. 329 | func (q Query) LastWithT(predicateFn interface{}) interface{} { 330 | 331 | predicateGenericFunc, err := newGenericFunc( 332 | "LastWithT", "predicateFn", predicateFn, 333 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 334 | ) 335 | if err != nil { 336 | panic(err) 337 | } 338 | 339 | predicateFunc := func(item interface{}) bool { 340 | return predicateGenericFunc.Call(item).(bool) 341 | } 342 | 343 | return q.LastWith(predicateFunc) 344 | } 345 | 346 | // Max returns the maximum value in a collection of values. 347 | func (q Query) Max() (r interface{}) { 348 | next := q.Iterate() 349 | item, ok := next() 350 | if !ok { 351 | return nil 352 | } 353 | 354 | compare := getComparer(item) 355 | r = item 356 | 357 | for item, ok := next(); ok; item, ok = next() { 358 | if compare(item, r) > 0 { 359 | r = item 360 | } 361 | } 362 | 363 | return 364 | } 365 | 366 | // Min returns the minimum value in a collection of values. 367 | func (q Query) Min() (r interface{}) { 368 | next := q.Iterate() 369 | item, ok := next() 370 | if !ok { 371 | return nil 372 | } 373 | 374 | compare := getComparer(item) 375 | r = item 376 | 377 | for item, ok := next(); ok; item, ok = next() { 378 | if compare(item, r) < 0 { 379 | r = item 380 | } 381 | } 382 | 383 | return 384 | } 385 | 386 | // Results iterates over a collection and returnes slice of interfaces 387 | func (q Query) Results() (r []interface{}) { 388 | next := q.Iterate() 389 | 390 | for item, ok := next(); ok; item, ok = next() { 391 | r = append(r, item) 392 | } 393 | 394 | return 395 | } 396 | 397 | // SequenceEqual determines whether two collections are equal. 398 | func (q Query) SequenceEqual(q2 Query) bool { 399 | next := q.Iterate() 400 | next2 := q2.Iterate() 401 | 402 | for item, ok := next(); ok; item, ok = next() { 403 | item2, ok2 := next2() 404 | if !ok2 || item != item2 { 405 | return false 406 | } 407 | } 408 | 409 | _, ok2 := next2() 410 | return !ok2 411 | } 412 | 413 | // Single returns the only element of a collection, and nil if there is not 414 | // exactly one element in the collection. 415 | func (q Query) Single() interface{} { 416 | next := q.Iterate() 417 | item, ok := next() 418 | if !ok { 419 | return nil 420 | } 421 | 422 | _, ok = next() 423 | if ok { 424 | return nil 425 | } 426 | 427 | return item 428 | } 429 | 430 | // SingleWith returns the only element of a collection that satisfies a 431 | // specified condition, and nil if more than one such element exists. 432 | func (q Query) SingleWith(predicate func(interface{}) bool) (r interface{}) { 433 | next := q.Iterate() 434 | found := false 435 | 436 | for item, ok := next(); ok; item, ok = next() { 437 | if predicate(item) { 438 | if found { 439 | return nil 440 | } 441 | 442 | found = true 443 | r = item 444 | } 445 | } 446 | 447 | return 448 | } 449 | 450 | // SingleWithT is the typed version of SingleWith. 451 | // 452 | // - predicateFn is of type "func(TSource) bool" 453 | // 454 | // NOTE: SingleWith has better performance than SingleWithT. 455 | func (q Query) SingleWithT(predicateFn interface{}) interface{} { 456 | predicateGenericFunc, err := newGenericFunc( 457 | "SingleWithT", "predicateFn", predicateFn, 458 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 459 | ) 460 | if err != nil { 461 | panic(err) 462 | } 463 | 464 | predicateFunc := func(item interface{}) bool { 465 | return predicateGenericFunc.Call(item).(bool) 466 | } 467 | 468 | return q.SingleWith(predicateFunc) 469 | } 470 | 471 | // SumInts computes the sum of a collection of numeric values. 472 | // 473 | // Values can be of any integer type: int, int8, int16, int32, int64. The result 474 | // is int64. Method returns zero if collection contains no elements. 475 | func (q Query) SumInts() (r int64) { 476 | next := q.Iterate() 477 | item, ok := next() 478 | if !ok { 479 | return 0 480 | } 481 | 482 | conv := getIntConverter(item) 483 | r = conv(item) 484 | 485 | for item, ok = next(); ok; item, ok = next() { 486 | r += conv(item) 487 | } 488 | 489 | return 490 | } 491 | 492 | // SumUInts computes the sum of a collection of numeric values. 493 | // 494 | // Values can be of any unsigned integer type: uint, uint8, uint16, uint32, 495 | // uint64. The result is uint64. Method returns zero if collection contains no 496 | // elements. 497 | func (q Query) SumUInts() (r uint64) { 498 | next := q.Iterate() 499 | item, ok := next() 500 | if !ok { 501 | return 0 502 | } 503 | 504 | conv := getUIntConverter(item) 505 | r = conv(item) 506 | 507 | for item, ok = next(); ok; item, ok = next() { 508 | r += conv(item) 509 | } 510 | 511 | return 512 | } 513 | 514 | // SumFloats computes the sum of a collection of numeric values. 515 | // 516 | // Values can be of any float type: float32 or float64. The result is float64. 517 | // Method returns zero if collection contains no elements. 518 | func (q Query) SumFloats() (r float64) { 519 | next := q.Iterate() 520 | item, ok := next() 521 | if !ok { 522 | return 0 523 | } 524 | 525 | conv := getFloatConverter(item) 526 | r = conv(item) 527 | 528 | for item, ok = next(); ok; item, ok = next() { 529 | r += conv(item) 530 | } 531 | 532 | return 533 | } 534 | 535 | // ToChannel iterates over a collection and outputs each element to a channel, 536 | // then closes it. 537 | func (q Query) ToChannel(result chan<- interface{}) { 538 | next := q.Iterate() 539 | 540 | for item, ok := next(); ok; item, ok = next() { 541 | result <- item 542 | } 543 | 544 | close(result) 545 | } 546 | 547 | // ToChannelT is the typed version of ToChannel. 548 | // 549 | // - result is of type "chan TSource" 550 | // 551 | // NOTE: ToChannel has better performance than ToChannelT. 552 | func (q Query) ToChannelT(result interface{}) { 553 | r := reflect.ValueOf(result) 554 | next := q.Iterate() 555 | 556 | for item, ok := next(); ok; item, ok = next() { 557 | r.Send(reflect.ValueOf(item)) 558 | } 559 | 560 | r.Close() 561 | } 562 | 563 | // ToMap iterates over a collection and populates result map with elements. 564 | // Collection elements have to be of KeyValue type to use this method. To 565 | // populate a map with elements of different type use ToMapBy method. ToMap 566 | // doesn't empty the result map before populating it. 567 | func (q Query) ToMap(result interface{}) { 568 | q.ToMapBy( 569 | result, 570 | func(i interface{}) interface{} { 571 | return i.(KeyValue).Key 572 | }, 573 | func(i interface{}) interface{} { 574 | return i.(KeyValue).Value 575 | }) 576 | } 577 | 578 | // ToMapBy iterates over a collection and populates the result map with 579 | // elements. Functions keySelector and valueSelector are executed for each 580 | // element of the collection to generate key and value for the map. Generated 581 | // key and value types must be assignable to the map's key and value types. 582 | // ToMapBy doesn't empty the result map before populating it. 583 | func (q Query) ToMapBy(result interface{}, 584 | keySelector func(interface{}) interface{}, 585 | valueSelector func(interface{}) interface{}) { 586 | res := reflect.ValueOf(result) 587 | m := reflect.Indirect(res) 588 | next := q.Iterate() 589 | 590 | for item, ok := next(); ok; item, ok = next() { 591 | key := reflect.ValueOf(keySelector(item)) 592 | value := reflect.ValueOf(valueSelector(item)) 593 | 594 | m.SetMapIndex(key, value) 595 | } 596 | 597 | res.Elem().Set(m) 598 | } 599 | 600 | // ToMapByT is the typed version of ToMapBy. 601 | // 602 | // - keySelectorFn is of type "func(TSource)TKey" 603 | // - valueSelectorFn is of type "func(TSource)TValue" 604 | // 605 | // NOTE: ToMapBy has better performance than ToMapByT. 606 | func (q Query) ToMapByT(result interface{}, 607 | keySelectorFn interface{}, valueSelectorFn interface{}) { 608 | keySelectorGenericFunc, err := newGenericFunc( 609 | "ToMapByT", "keySelectorFn", keySelectorFn, 610 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 611 | ) 612 | if err != nil { 613 | panic(err) 614 | } 615 | 616 | keySelectorFunc := func(item interface{}) interface{} { 617 | return keySelectorGenericFunc.Call(item) 618 | } 619 | 620 | valueSelectorGenericFunc, err := newGenericFunc( 621 | "ToMapByT", "valueSelectorFn", valueSelectorFn, 622 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 623 | ) 624 | if err != nil { 625 | panic(err) 626 | } 627 | 628 | valueSelectorFunc := func(item interface{}) interface{} { 629 | return valueSelectorGenericFunc.Call(item) 630 | } 631 | 632 | q.ToMapBy(result, keySelectorFunc, valueSelectorFunc) 633 | } 634 | 635 | // ToSlice iterates over a collection and saves the results in the slice pointed 636 | // by v. It overwrites the existing slice, starting from index 0. 637 | // 638 | // If the slice pointed by v has sufficient capacity, v will be pointed to a 639 | // resliced slice. If it does not, a new underlying array will be allocated and 640 | // v will point to it. 641 | func (q Query) ToSlice(v interface{}) { 642 | res := reflect.ValueOf(v) 643 | slice := reflect.Indirect(res) 644 | 645 | cap := slice.Cap() 646 | res.Elem().Set(slice.Slice(0, cap)) // make len(slice)==cap(slice) from now on 647 | 648 | next := q.Iterate() 649 | index := 0 650 | for item, ok := next(); ok; item, ok = next() { 651 | if index >= cap { 652 | slice, cap = grow(slice) 653 | } 654 | slice.Index(index).Set(reflect.ValueOf(item)) 655 | index++ 656 | } 657 | 658 | // reslice the len(res)==cap(res) actual res size 659 | res.Elem().Set(slice.Slice(0, index)) 660 | } 661 | 662 | // grow grows the slice s by doubling its capacity, then it returns the new 663 | // slice (resliced to its full capacity) and the new capacity. 664 | func grow(s reflect.Value) (v reflect.Value, newCap int) { 665 | cap := s.Cap() 666 | if cap == 0 { 667 | cap = 1 668 | } else { 669 | cap *= 2 670 | } 671 | newSlice := reflect.MakeSlice(s.Type(), cap, cap) 672 | reflect.Copy(newSlice, s) 673 | return newSlice, cap 674 | } 675 | -------------------------------------------------------------------------------- /result_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import ( 4 | "math" 5 | "reflect" 6 | "testing" 7 | "unsafe" 8 | ) 9 | 10 | func TestAll(t *testing.T) { 11 | input := []int{2, 4, 6, 8} 12 | 13 | r1 := From(input).All(func(i interface{}) bool { 14 | return i.(int)%2 == 0 15 | }) 16 | r2 := From(input).All(func(i interface{}) bool { 17 | return i.(int)%2 != 0 18 | }) 19 | 20 | if !r1 { 21 | t.Errorf("From(%v).All()=%v", input, r1) 22 | } 23 | 24 | if r2 { 25 | t.Errorf("From(%v).All()=%v", input, r2) 26 | } 27 | } 28 | 29 | func TestAllT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 30 | mustPanicWithError(t, "AllT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int)int'", func() { 31 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).AllT(func(item int) int { return item + 2 }) 32 | }) 33 | } 34 | 35 | func TestAny(t *testing.T) { 36 | tests := []struct { 37 | input interface{} 38 | want bool 39 | }{ 40 | {[]int{1, 2, 2, 3, 1}, true}, 41 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, true}, 42 | {"sstr", true}, 43 | {[]int{}, false}, 44 | } 45 | 46 | for _, test := range tests { 47 | if r := From(test.input).Any(); r != test.want { 48 | t.Errorf("From(%v).Any()=%v expected %v", test.input, r, test.want) 49 | } 50 | } 51 | } 52 | 53 | func TestAnyWith(t *testing.T) { 54 | tests := []struct { 55 | input interface{} 56 | want bool 57 | }{ 58 | {[]int{1, 2, 2, 3, 1}, false}, 59 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, true}, 60 | {[]int{}, false}, 61 | } 62 | 63 | for _, test := range tests { 64 | if r := From(test.input).AnyWith(func(i interface{}) bool { 65 | return i.(int) == 4 66 | }); r != test.want { 67 | t.Errorf("From(%v).Any()=%v expected %v", test.input, r, test.want) 68 | } 69 | } 70 | } 71 | 72 | func TestAnyWithT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 73 | mustPanicWithError(t, "AnyWithT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int)int'", func() { 74 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).AnyWithT(func(item int) int { return item + 2 }) 75 | }) 76 | } 77 | 78 | func TestAverage(t *testing.T) { 79 | tests := []struct { 80 | input interface{} 81 | want float64 82 | }{ 83 | {[]int{1, 2, 2, 3, 1}, 1.8}, 84 | {[5]uint{1, 2, 5, 7, 10}, 5.}, 85 | {[]float32{1., 1.}, 1.}, 86 | } 87 | 88 | for _, test := range tests { 89 | if r := From(test.input).Average(); r != test.want { 90 | t.Errorf("From(%v).Average()=%v expected %v", test.input, r, test.want) 91 | } 92 | } 93 | } 94 | 95 | func TestAverageForNaN(t *testing.T) { 96 | if r := From([]int{}).Average(); !math.IsNaN(r) { 97 | t.Errorf("From([]int{}).Average()=%v expected %v", r, math.NaN()) 98 | } 99 | } 100 | 101 | func TestContains(t *testing.T) { 102 | tests := []struct { 103 | input interface{} 104 | value interface{} 105 | want bool 106 | }{ 107 | {[]int{1, 2, 2, 3, 1}, 10, false}, 108 | {[5]uint{1, 2, 5, 7, 10}, uint(5), true}, 109 | {[]float32{}, 1., false}, 110 | } 111 | 112 | for _, test := range tests { 113 | if r := From(test.input).Contains(test.value); r != test.want { 114 | t.Errorf("From(%v).Contains(%v)=%v expected %v", test.input, test.value, r, test.want) 115 | } 116 | } 117 | } 118 | 119 | func TestCount(t *testing.T) { 120 | tests := []struct { 121 | input interface{} 122 | want int 123 | }{ 124 | {[]int{1, 2, 2, 3, 1}, 5}, 125 | {[7]uint{1, 2, 5, 7, 10, 12, 15}, 7}, 126 | {[]float32{}, 0}, 127 | } 128 | 129 | for _, test := range tests { 130 | if r := From(test.input).Count(); r != test.want { 131 | t.Errorf("From(%v).Count()=%v expected %v", test.input, r, test.want) 132 | } 133 | } 134 | } 135 | 136 | func TestCountWith(t *testing.T) { 137 | tests := []struct { 138 | input interface{} 139 | want int 140 | }{ 141 | {[]int{1, 2, 2, 3, 1}, 4}, 142 | {[]int{}, 0}, 143 | } 144 | 145 | for _, test := range tests { 146 | if r := From(test.input).CountWith(func(i interface{}) bool { 147 | return i.(int) <= 2 148 | }); r != test.want { 149 | t.Errorf("From(%v).CountWith()=%v expected %v", test.input, r, test.want) 150 | } 151 | } 152 | } 153 | 154 | func TestCountWithT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 155 | mustPanicWithError(t, "CountWithT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int)int'", func() { 156 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).CountWithT(func(item int) int { return item + 2 }) 157 | }) 158 | } 159 | 160 | func TestFirst(t *testing.T) { 161 | tests := []struct { 162 | input interface{} 163 | want interface{} 164 | }{ 165 | {[]int{1, 2, 2, 3, 1}, 1}, 166 | {[]int{}, nil}, 167 | } 168 | 169 | for _, test := range tests { 170 | if r := From(test.input).First(); r != test.want { 171 | t.Errorf("From(%v).First()=%v expected %v", test.input, r, test.want) 172 | } 173 | } 174 | } 175 | 176 | func TestFirstWith(t *testing.T) { 177 | tests := []struct { 178 | input interface{} 179 | want interface{} 180 | }{ 181 | {[]int{1, 2, 2, 3, 1}, 3}, 182 | {[]int{}, nil}, 183 | } 184 | 185 | for _, test := range tests { 186 | if r := From(test.input).FirstWith(func(i interface{}) bool { 187 | return i.(int) > 2 188 | }); r != test.want { 189 | t.Errorf("From(%v).FirstWith()=%v expected %v", test.input, r, test.want) 190 | } 191 | } 192 | } 193 | 194 | func TestFirstWithT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 195 | mustPanicWithError(t, "FirstWithT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int)int'", func() { 196 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).FirstWithT(func(item int) int { return item + 2 }) 197 | }) 198 | } 199 | 200 | func TestForEach(t *testing.T) { 201 | tests := []struct { 202 | input interface{} 203 | want interface{} 204 | }{ 205 | {[5]int{1, 2, 2, 35, 111}, []int{2, 4, 4, 70, 222}}, 206 | {[]int{}, []int{}}, 207 | } 208 | 209 | for _, test := range tests { 210 | output := []int{} 211 | From(test.input).ForEach(func(item interface{}) { 212 | output = append(output, item.(int)*2) 213 | }) 214 | 215 | if !reflect.DeepEqual(output, test.want) { 216 | t.Fatalf("From(%#v).ForEach()=%#v expected=%#v", test.input, output, test.want) 217 | } 218 | } 219 | } 220 | 221 | func TestForEachT_PanicWhenActionFnIsInvalid(t *testing.T) { 222 | mustPanicWithError(t, "ForEachT: parameter [actionFn] has a invalid function signature. Expected: 'func(T)', actual: 'func(int,int)'", func() { 223 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).ForEachT(func(item, idx int) { item = item + 2 }) 224 | }) 225 | } 226 | 227 | func TestForEachIndexed(t *testing.T) { 228 | tests := []struct { 229 | input interface{} 230 | want interface{} 231 | }{ 232 | {[5]int{1, 2, 2, 35, 111}, []int{1, 3, 4, 38, 115}}, 233 | {[]int{}, []int{}}, 234 | } 235 | 236 | for _, test := range tests { 237 | output := []int{} 238 | From(test.input).ForEachIndexed(func(index int, item interface{}) { 239 | output = append(output, item.(int)+index) 240 | }) 241 | 242 | if !reflect.DeepEqual(output, test.want) { 243 | t.Fatalf("From(%#v).ForEachIndexed()=%#v expected=%#v", test.input, output, test.want) 244 | } 245 | } 246 | } 247 | 248 | func TestForEachIndexedT_PanicWhenActionFnIsInvalid(t *testing.T) { 249 | mustPanicWithError(t, "ForEachIndexedT: parameter [actionFn] has a invalid function signature. Expected: 'func(int,T)', actual: 'func(int)'", func() { 250 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).ForEachIndexedT(func(item int) { item = item + 2 }) 251 | }) 252 | } 253 | 254 | func TestLast(t *testing.T) { 255 | tests := []struct { 256 | input interface{} 257 | want interface{} 258 | }{ 259 | {[]int{1, 2, 2, 3, 1}, 1}, 260 | {[]int{}, nil}, 261 | } 262 | 263 | for _, test := range tests { 264 | if r := From(test.input).Last(); r != test.want { 265 | t.Errorf("From(%v).Last()=%v expected %v", test.input, r, test.want) 266 | } 267 | } 268 | } 269 | 270 | func TestLastWith(t *testing.T) { 271 | tests := []struct { 272 | input interface{} 273 | want interface{} 274 | }{ 275 | {[]int{1, 2, 2, 3, 1, 4, 2, 5, 1, 1}, 5}, 276 | {[]int{}, nil}, 277 | } 278 | 279 | for _, test := range tests { 280 | if r := From(test.input).LastWith(func(i interface{}) bool { 281 | return i.(int) > 2 282 | }); r != test.want { 283 | t.Errorf("From(%v).LastWith()=%v expected %v", test.input, r, test.want) 284 | } 285 | } 286 | } 287 | 288 | func TestLastWithT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 289 | mustPanicWithError(t, "LastWithT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int)int'", func() { 290 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).LastWithT(func(item int) int { return item + 2 }) 291 | }) 292 | } 293 | 294 | func TestMax(t *testing.T) { 295 | tests := []struct { 296 | input interface{} 297 | want interface{} 298 | }{ 299 | {[]int{1, 2, 2, 3, 1}, 3}, 300 | {[]int{1}, 1}, 301 | {[]int{}, nil}, 302 | } 303 | 304 | for _, test := range tests { 305 | if r := From(test.input).Max(); r != test.want { 306 | t.Errorf("From(%v).Max()=%v expected %v", test.input, r, test.want) 307 | } 308 | } 309 | } 310 | 311 | func TestMin(t *testing.T) { 312 | tests := []struct { 313 | input interface{} 314 | want interface{} 315 | }{ 316 | {[]int{1, 2, 2, 3, 0}, 0}, 317 | {[]int{1}, 1}, 318 | {[]int{}, nil}, 319 | } 320 | 321 | for _, test := range tests { 322 | if r := From(test.input).Min(); r != test.want { 323 | t.Errorf("From(%v).Min()=%v expected %v", test.input, r, test.want) 324 | } 325 | } 326 | } 327 | 328 | func TestResults(t *testing.T) { 329 | input := []int{1, 2, 3} 330 | want := []interface{}{1, 2, 3} 331 | 332 | if r := From(input).Results(); !reflect.DeepEqual(r, want) { 333 | t.Errorf("From(%v).Raw()=%v expected %v", input, r, want) 334 | } 335 | } 336 | 337 | func TestSequenceEqual(t *testing.T) { 338 | tests := []struct { 339 | input interface{} 340 | input2 interface{} 341 | want bool 342 | }{ 343 | {[]int{1, 2, 2, 3, 1}, []int{4, 6}, false}, 344 | {[]int{1, -1, 100}, []int{1, -1, 100}, true}, 345 | {[]int{}, []int{}, true}, 346 | } 347 | 348 | for _, test := range tests { 349 | if r := From(test.input).SequenceEqual(From(test.input2)); r != test.want { 350 | t.Errorf("From(%v).SequenceEqual(%v)=%v expected %v", test.input, test.input2, r, test.want) 351 | } 352 | } 353 | } 354 | 355 | func TestSingle(t *testing.T) { 356 | tests := []struct { 357 | input interface{} 358 | want interface{} 359 | }{ 360 | {[]int{1, 2, 2, 3, 1}, nil}, 361 | {[]int{1}, 1}, 362 | {[]int{}, nil}, 363 | } 364 | 365 | for _, test := range tests { 366 | if r := From(test.input).Single(); r != test.want { 367 | t.Errorf("From(%v).Single()=%v expected %v", test.input, r, test.want) 368 | } 369 | } 370 | } 371 | 372 | func TestSingleWith(t *testing.T) { 373 | tests := []struct { 374 | input interface{} 375 | want interface{} 376 | }{ 377 | {[]int{1, 2, 2, 3, 1}, 3}, 378 | {[]int{1, 1, 1}, nil}, 379 | {[]int{5, 1, 1, 10, 2, 2}, nil}, 380 | {[]int{}, nil}, 381 | } 382 | 383 | for _, test := range tests { 384 | if r := From(test.input).SingleWith(func(i interface{}) bool { 385 | return i.(int) > 2 386 | }); r != test.want { 387 | t.Errorf("From(%v).SingleWith()=%v expected %v", test.input, r, test.want) 388 | } 389 | } 390 | } 391 | 392 | func TestSingleWithT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 393 | mustPanicWithError(t, "SingleWithT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int)int'", func() { 394 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).SingleWithT(func(item int) int { return item + 2 }) 395 | }) 396 | } 397 | 398 | func TestSumInts(t *testing.T) { 399 | tests := []struct { 400 | input interface{} 401 | want int64 402 | }{ 403 | {[]int{1, 2, 2, 3, 1}, 9}, 404 | {[]int{1}, 1}, 405 | {[]int{}, 0}, 406 | } 407 | 408 | for _, test := range tests { 409 | if r := From(test.input).SumInts(); r != test.want { 410 | t.Errorf("From(%v).SumInts()=%v expected %v", test.input, r, test.want) 411 | } 412 | } 413 | } 414 | 415 | func TestSumUInts(t *testing.T) { 416 | tests := []struct { 417 | input interface{} 418 | want uint64 419 | }{ 420 | {[]uint{1, 2, 2, 3, 1}, 9}, 421 | {[]uint{1}, 1}, 422 | {[]uint{}, 0}, 423 | } 424 | 425 | for _, test := range tests { 426 | if r := From(test.input).SumUInts(); r != test.want { 427 | t.Errorf("From(%v).SumInts()=%v expected %v", test.input, r, test.want) 428 | } 429 | } 430 | } 431 | 432 | func TestSumFloats(t *testing.T) { 433 | tests := []struct { 434 | input interface{} 435 | want float64 436 | }{ 437 | {[]float32{1., 2., 2., 3., 1.}, 9.}, 438 | {[]float64{1.}, 1.}, 439 | {[]float32{}, 0.}, 440 | } 441 | 442 | for _, test := range tests { 443 | if r := From(test.input).SumFloats(); r != test.want { 444 | t.Errorf("From(%v).SumFloats()=%v expected %v", test.input, r, test.want) 445 | } 446 | } 447 | } 448 | 449 | func TestToChannel(t *testing.T) { 450 | c := make(chan interface{}) 451 | input := []int{1, 2, 3, 4, 5} 452 | 453 | go func() { 454 | From(input).ToChannel(c) 455 | }() 456 | 457 | result := []int{} 458 | for value := range c { 459 | result = append(result, value.(int)) 460 | } 461 | 462 | if !reflect.DeepEqual(result, input) { 463 | t.Errorf("From(%v).ToChannel()=%v expected %v", input, result, input) 464 | } 465 | } 466 | 467 | func TestToChannelT(t *testing.T) { 468 | c := make(chan string) 469 | input := []string{"1", "2", "3", "4", "5"} 470 | 471 | go From(input).ToChannelT(c) 472 | 473 | result := []string{} 474 | for value := range c { 475 | result = append(result, value) 476 | } 477 | 478 | if !reflect.DeepEqual(result, input) { 479 | t.Errorf("From(%v).ToChannelT()=%v expected %v", input, result, input) 480 | } 481 | } 482 | 483 | func TestToMap(t *testing.T) { 484 | input := make(map[int]bool) 485 | input[1] = true 486 | input[2] = false 487 | input[3] = true 488 | 489 | result := make(map[int]bool) 490 | From(input).ToMap(&result) 491 | 492 | if !reflect.DeepEqual(result, input) { 493 | t.Errorf("From(%v).ToMap()=%v expected %v", input, result, input) 494 | } 495 | } 496 | 497 | func TestToMapBy(t *testing.T) { 498 | input := make(map[int]bool) 499 | input[1] = true 500 | input[2] = false 501 | input[3] = true 502 | 503 | result := make(map[int]bool) 504 | From(input).ToMapBy(&result, 505 | func(i interface{}) interface{} { 506 | return i.(KeyValue).Key 507 | }, 508 | func(i interface{}) interface{} { 509 | return i.(KeyValue).Value 510 | }) 511 | 512 | if !reflect.DeepEqual(result, input) { 513 | t.Errorf("From(%v).ToMapBy()=%v expected %v", input, result, input) 514 | } 515 | } 516 | 517 | func TestToMapByT_PanicWhenKeySelectorFnIsInvalid(t *testing.T) { 518 | mustPanicWithError(t, "ToMapByT: parameter [keySelectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 519 | result := make(map[int]bool) 520 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).ToMapByT( 521 | &result, 522 | func(item, j int) int { return item + 2 }, 523 | func(item int) int { return item + 2 }, 524 | ) 525 | }) 526 | } 527 | 528 | func TestToMapByT_PanicWhenValueSelectorFnIsInvalid(t *testing.T) { 529 | mustPanicWithError(t, "ToMapByT: parameter [valueSelectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 530 | result := make(map[int]bool) 531 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).ToMapByT( 532 | &result, 533 | func(item int) int { return item + 2 }, 534 | func(item, j int) int { return item + 2 }, 535 | ) 536 | }) 537 | } 538 | 539 | func TestToSlice(t *testing.T) { 540 | tests := []struct { 541 | input []int 542 | output []int 543 | want []int 544 | wantedOutputCap int 545 | outputIsANewSlice bool 546 | }{ 547 | // output is nil slice 548 | { 549 | []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 550 | nil, 551 | []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 552 | 16, 553 | true}, 554 | // output is empty slice (cap=0) 555 | { 556 | []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 557 | []int{}, 558 | []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 559 | 16, 560 | true}, 561 | // ToSlice() overwrites existing elements and reslices. 562 | {[]int{1, 2, 3}, 563 | []int{99, 98, 97, 96, 95}, 564 | []int{1, 2, 3}, 565 | 5, 566 | false}, 567 | // cap(out)>len(result): we get the same slice, resliced. cap unchanged. 568 | {[]int{1, 2, 3, 4, 5}, 569 | make([]int, 0, 11), 570 | []int{1, 2, 3, 4, 5}, 571 | 11, 572 | false}, 573 | // cap(out)==len(result): we get the same slice, cap unchanged. 574 | {[]int{1, 2, 3, 4, 5}, 575 | make([]int, 0, 5), 576 | []int{1, 2, 3, 4, 5}, 577 | 5, 578 | false}, 579 | // cap(out) 52 -> 104) 586 | {make([]int, 100), 587 | make([]int, 0, 26), 588 | make([]int, 100), 589 | 104, 590 | true}, 591 | // len(out) > len(result): we get the same slice with len(out)=len(result) and cap unchanged: cap(out')==cap(out) 592 | {[]int{1, 2, 3, 4, 5}, 593 | make([]int, 0, 50), 594 | []int{1, 2, 3, 4, 5}, 595 | 50, 596 | false}, 597 | } 598 | 599 | for c, test := range tests { 600 | initialOutputValue := test.output 601 | From(test.input).ToSlice(&test.output) 602 | modifiedOutputValue := test.output 603 | 604 | // test slice values 605 | if !reflect.DeepEqual(test.output, test.want) { 606 | t.Fatalf("case #%d: From(%#v).ToSlice()=%#v expected=%#v", c, test.input, test.output, test.want) 607 | } 608 | 609 | // test capacity of output slice 610 | if cap(test.output) != test.wantedOutputCap { 611 | t.Fatalf("case #%d: cap(output)=%d expected=%d", c, cap(test.output), test.wantedOutputCap) 612 | } 613 | 614 | // test if a new slice is allocated 615 | inPtr := (*reflect.SliceHeader)(unsafe.Pointer(&initialOutputValue)).Data 616 | outPtr := (*reflect.SliceHeader)(unsafe.Pointer(&modifiedOutputValue)).Data 617 | isNewSlice := inPtr != outPtr 618 | if isNewSlice != test.outputIsANewSlice { 619 | t.Fatalf("case #%d: isNewSlice=%v (in=0x%X out=0x%X) expected=%v", c, isNewSlice, inPtr, outPtr, test.outputIsANewSlice) 620 | } 621 | } 622 | } 623 | -------------------------------------------------------------------------------- /reverse.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Reverse inverts the order of the elements in a collection. 4 | // 5 | // Unlike OrderBy, this sorting method does not consider the actual values 6 | // themselves in determining the order. Rather, it just returns the elements in 7 | // the reverse order from which they are produced by the underlying source. 8 | func (q Query) Reverse() Query { 9 | return Query{ 10 | Iterate: func() Iterator { 11 | next := q.Iterate() 12 | 13 | items := []interface{}{} 14 | for item, ok := next(); ok; item, ok = next() { 15 | items = append(items, item) 16 | } 17 | 18 | index := len(items) - 1 19 | return func() (item interface{}, ok bool) { 20 | if index < 0 { 21 | return 22 | } 23 | 24 | item, ok = items[index], true 25 | index-- 26 | return 27 | } 28 | }, 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /reverse_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestReverse(t *testing.T) { 6 | tests := []struct { 7 | input interface{} 8 | want []interface{} 9 | }{ 10 | {[]int{1, 2, 3}, []interface{}{3, 2, 1}}, 11 | } 12 | 13 | for _, test := range tests { 14 | if q := From(test.input).Reverse(); !validateQuery(q, test.want) { 15 | t.Errorf("From(%v).Reverse()=%v expected %v", test.input, toSlice(q), test.want) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /select.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Select projects each element of a collection into a new form. Returns a query 4 | // with the result of invoking the transform function on each element of 5 | // original source. 6 | // 7 | // This projection method requires the transform function, selector, to produce 8 | // one value for each value in the source collection. If selector returns a 9 | // value that is itself a collection, it is up to the consumer to traverse the 10 | // subcollections manually. In such a situation, it might be better for your 11 | // query to return a single coalesced collection of values. To achieve this, use 12 | // the SelectMany method instead of Select. Although SelectMany works similarly 13 | // to Select, it differs in that the transform function returns a collection 14 | // that is then expanded by SelectMany before it is returned. 15 | func (q Query) Select(selector func(interface{}) interface{}) Query { 16 | return Query{ 17 | Iterate: func() Iterator { 18 | next := q.Iterate() 19 | 20 | return func() (item interface{}, ok bool) { 21 | var it interface{} 22 | it, ok = next() 23 | if ok { 24 | item = selector(it) 25 | } 26 | 27 | return 28 | } 29 | }, 30 | } 31 | } 32 | 33 | // SelectT is the typed version of Select. 34 | // - selectorFn is of type "func(TSource)TResult" 35 | // NOTE: Select has better performance than SelectT. 36 | func (q Query) SelectT(selectorFn interface{}) Query { 37 | 38 | selectGenericFunc, err := newGenericFunc( 39 | "SelectT", "selectorFn", selectorFn, 40 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))), 41 | ) 42 | if err != nil { 43 | panic(err) 44 | } 45 | 46 | selectorFunc := func(item interface{}) interface{} { 47 | return selectGenericFunc.Call(item) 48 | } 49 | 50 | return q.Select(selectorFunc) 51 | } 52 | 53 | // SelectIndexed projects each element of a collection into a new form by 54 | // incorporating the element's index. Returns a query with the result of 55 | // invoking the transform function on each element of original source. 56 | // 57 | // The first argument to selector represents the zero-based index of that 58 | // element in the source collection. This can be useful if the elements are in a 59 | // known order and you want to do something with an element at a particular 60 | // index, for example. It can also be useful if you want to retrieve the index 61 | // of one or more elements. The second argument to selector represents the 62 | // element to process. 63 | // 64 | // This projection method requires the transform function, selector, to produce 65 | // one value for each value in the source collection. If selector returns a 66 | // value that is itself a collection, it is up to the consumer to traverse the 67 | // subcollections manually. In such a situation, it might be better for your 68 | // query to return a single coalesced collection of values. To achieve this, use 69 | // the SelectMany method instead of Select. Although SelectMany works similarly 70 | // to Select, it differs in that the transform function returns a collection 71 | // that is then expanded by SelectMany before it is returned. 72 | func (q Query) SelectIndexed(selector func(int, interface{}) interface{}) Query { 73 | return Query{ 74 | Iterate: func() Iterator { 75 | next := q.Iterate() 76 | index := 0 77 | 78 | return func() (item interface{}, ok bool) { 79 | var it interface{} 80 | it, ok = next() 81 | if ok { 82 | item = selector(index, it) 83 | index++ 84 | } 85 | 86 | return 87 | } 88 | }, 89 | } 90 | } 91 | 92 | // SelectIndexedT is the typed version of SelectIndexed. 93 | // - selectorFn is of type "func(int,TSource)TResult" 94 | // NOTE: SelectIndexed has better performance than SelectIndexedT. 95 | func (q Query) SelectIndexedT(selectorFn interface{}) Query { 96 | selectGenericFunc, err := newGenericFunc( 97 | "SelectIndexedT", "selectorFn", selectorFn, 98 | simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(genericType))), 99 | ) 100 | if err != nil { 101 | panic(err) 102 | } 103 | 104 | selectorFunc := func(index int, item interface{}) interface{} { 105 | return selectGenericFunc.Call(index, item) 106 | } 107 | 108 | return q.SelectIndexed(selectorFunc) 109 | } 110 | -------------------------------------------------------------------------------- /select_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | ) 7 | 8 | func TestSelect(t *testing.T) { 9 | tests := []struct { 10 | input interface{} 11 | selector func(interface{}) interface{} 12 | output []interface{} 13 | }{ 14 | {[]int{1, 2, 3}, func(i interface{}) interface{} { 15 | return i.(int) * 2 16 | }, []interface{}{2, 4, 6}}, 17 | {"str", func(i interface{}) interface{} { 18 | return string(i.(rune)) + "1" 19 | }, []interface{}{"s1", "t1", "r1"}}, 20 | } 21 | 22 | for _, test := range tests { 23 | if q := From(test.input).Select(test.selector); !validateQuery(q, test.output) { 24 | t.Errorf("From(%v).Select()=%v expected %v", test.input, toSlice(q), test.output) 25 | } 26 | } 27 | } 28 | 29 | func TestSelectT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 30 | mustPanicWithError(t, "SelectT: parameter [selectorFn] has a invalid function signature. Expected: 'func(T)T', actual: 'func(int,int)int'", func() { 31 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).SelectT(func(item, idx int) int { return item + 2 }) 32 | }) 33 | } 34 | 35 | func TestSelectIndexed(t *testing.T) { 36 | tests := []struct { 37 | input interface{} 38 | selector func(int, interface{}) interface{} 39 | output []interface{} 40 | }{ 41 | {[]int{1, 2, 3}, func(i int, x interface{}) interface{} { 42 | return x.(int) * i 43 | }, []interface{}{0, 2, 6}}, 44 | {"str", func(i int, x interface{}) interface{} { 45 | return string(x.(rune)) + strconv.Itoa(i) 46 | }, []interface{}{"s0", "t1", "r2"}}, 47 | } 48 | 49 | for _, test := range tests { 50 | if q := From(test.input).SelectIndexed(test.selector); !validateQuery(q, test.output) { 51 | t.Errorf("From(%v).SelectIndexed()=%v expected %v", test.input, toSlice(q), test.output) 52 | } 53 | } 54 | } 55 | 56 | func TestSelectIndexedT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 57 | mustPanicWithError(t, "SelectIndexedT: parameter [selectorFn] has a invalid function signature. Expected: 'func(int,T)T', actual: 'func(string,int)int'", func() { 58 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).SelectIndexedT(func(index string, item int) int { return item + 2 }) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /selectmany.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // SelectMany projects each element of a collection to a Query, iterates and 4 | // flattens the resulting collection into one collection. 5 | func (q Query) SelectMany(selector func(interface{}) Query) Query { 6 | return Query{ 7 | Iterate: func() Iterator { 8 | outernext := q.Iterate() 9 | var inner interface{} 10 | var innernext Iterator 11 | 12 | return func() (item interface{}, ok bool) { 13 | for !ok { 14 | if inner == nil { 15 | inner, ok = outernext() 16 | if !ok { 17 | return 18 | } 19 | 20 | innernext = selector(inner).Iterate() 21 | } 22 | 23 | item, ok = innernext() 24 | if !ok { 25 | inner = nil 26 | } 27 | } 28 | 29 | return 30 | } 31 | }, 32 | } 33 | } 34 | 35 | // SelectManyT is the typed version of SelectMany. 36 | // 37 | // - selectorFn is of type "func(TSource)Query" 38 | // 39 | // NOTE: SelectMany has better performance than SelectManyT. 40 | func (q Query) SelectManyT(selectorFn interface{}) Query { 41 | 42 | selectManyGenericFunc, err := newGenericFunc( 43 | "SelectManyT", "selectorFn", selectorFn, 44 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(Query))), 45 | ) 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | selectorFunc := func(inner interface{}) Query { 51 | return selectManyGenericFunc.Call(inner).(Query) 52 | } 53 | return q.SelectMany(selectorFunc) 54 | 55 | } 56 | 57 | // SelectManyIndexed projects each element of a collection to a Query, iterates 58 | // and flattens the resulting collection into one collection. 59 | // 60 | // The first argument to selector represents the zero-based index of that 61 | // element in the source collection. This can be useful if the elements are in a 62 | // known order and you want to do something with an element at a particular 63 | // index, for example. It can also be useful if you want to retrieve the index 64 | // of one or more elements. The second argument to selector represents the 65 | // element to process. 66 | func (q Query) SelectManyIndexed(selector func(int, interface{}) Query) Query { 67 | return Query{ 68 | Iterate: func() Iterator { 69 | outernext := q.Iterate() 70 | index := 0 71 | var inner interface{} 72 | var innernext Iterator 73 | 74 | return func() (item interface{}, ok bool) { 75 | for !ok { 76 | if inner == nil { 77 | inner, ok = outernext() 78 | if !ok { 79 | return 80 | } 81 | 82 | innernext = selector(index, inner).Iterate() 83 | index++ 84 | } 85 | 86 | item, ok = innernext() 87 | if !ok { 88 | inner = nil 89 | } 90 | } 91 | 92 | return 93 | } 94 | }, 95 | } 96 | } 97 | 98 | // SelectManyIndexedT is the typed version of SelectManyIndexed. 99 | // 100 | // - selectorFn is of type "func(int,TSource)Query" 101 | // 102 | // NOTE: SelectManyIndexed has better performance than SelectManyIndexedT. 103 | func (q Query) SelectManyIndexedT(selectorFn interface{}) Query { 104 | 105 | selectManyIndexedGenericFunc, err := newGenericFunc( 106 | "SelectManyIndexedT", "selectorFn", selectorFn, 107 | simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(Query))), 108 | ) 109 | if err != nil { 110 | panic(err) 111 | } 112 | 113 | selectorFunc := func(index int, inner interface{}) Query { 114 | return selectManyIndexedGenericFunc.Call(index, inner).(Query) 115 | } 116 | 117 | return q.SelectManyIndexed(selectorFunc) 118 | } 119 | 120 | // SelectManyBy projects each element of a collection to a Query, iterates and 121 | // flattens the resulting collection into one collection, and invokes a result 122 | // selector function on each element therein. 123 | func (q Query) SelectManyBy(selector func(interface{}) Query, 124 | resultSelector func(interface{}, interface{}) interface{}) Query { 125 | 126 | return Query{ 127 | Iterate: func() Iterator { 128 | outernext := q.Iterate() 129 | var outer interface{} 130 | var innernext Iterator 131 | 132 | return func() (item interface{}, ok bool) { 133 | for !ok { 134 | if outer == nil { 135 | outer, ok = outernext() 136 | if !ok { 137 | return 138 | } 139 | 140 | innernext = selector(outer).Iterate() 141 | } 142 | 143 | item, ok = innernext() 144 | if !ok { 145 | outer = nil 146 | } 147 | } 148 | 149 | item = resultSelector(item, outer) 150 | return 151 | } 152 | }, 153 | } 154 | } 155 | 156 | // SelectManyByT is the typed version of SelectManyBy. 157 | // 158 | // - selectorFn is of type "func(TSource)Query" 159 | // - resultSelectorFn is of type "func(TSource,TCollection)TResult" 160 | // 161 | // NOTE: SelectManyBy has better performance than SelectManyByT. 162 | func (q Query) SelectManyByT(selectorFn interface{}, 163 | resultSelectorFn interface{}) Query { 164 | 165 | selectorGenericFunc, err := newGenericFunc( 166 | "SelectManyByT", "selectorFn", selectorFn, 167 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(Query))), 168 | ) 169 | if err != nil { 170 | panic(err) 171 | } 172 | 173 | selectorFunc := func(outer interface{}) Query { 174 | return selectorGenericFunc.Call(outer).(Query) 175 | } 176 | 177 | resultSelectorGenericFunc, err := newGenericFunc( 178 | "SelectManyByT", "resultSelectorFn", resultSelectorFn, 179 | simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), 180 | ) 181 | if err != nil { 182 | panic(err) 183 | } 184 | 185 | resultSelectorFunc := func(outer interface{}, item interface{}) interface{} { 186 | return resultSelectorGenericFunc.Call(outer, item) 187 | } 188 | 189 | return q.SelectManyBy(selectorFunc, resultSelectorFunc) 190 | } 191 | 192 | // SelectManyByIndexed projects each element of a collection to a Query, 193 | // iterates and flattens the resulting collection into one collection, and 194 | // invokes a result selector function on each element therein. The index of each 195 | // source element is used in the intermediate projected form of that element. 196 | func (q Query) SelectManyByIndexed(selector func(int, interface{}) Query, 197 | resultSelector func(interface{}, interface{}) interface{}) Query { 198 | 199 | return Query{ 200 | Iterate: func() Iterator { 201 | outernext := q.Iterate() 202 | index := 0 203 | var outer interface{} 204 | var innernext Iterator 205 | 206 | return func() (item interface{}, ok bool) { 207 | for !ok { 208 | if outer == nil { 209 | outer, ok = outernext() 210 | if !ok { 211 | return 212 | } 213 | 214 | innernext = selector(index, outer).Iterate() 215 | index++ 216 | } 217 | 218 | item, ok = innernext() 219 | if !ok { 220 | outer = nil 221 | } 222 | } 223 | 224 | item = resultSelector(item, outer) 225 | return 226 | } 227 | }, 228 | } 229 | } 230 | 231 | // SelectManyByIndexedT is the typed version of SelectManyByIndexed. 232 | // 233 | // - selectorFn is of type "func(int,TSource)Query" 234 | // - resultSelectorFn is of type "func(TSource,TCollection)TResult" 235 | // 236 | // NOTE: SelectManyByIndexed has better performance than 237 | // SelectManyByIndexedT. 238 | func (q Query) SelectManyByIndexedT(selectorFn interface{}, 239 | resultSelectorFn interface{}) Query { 240 | selectorGenericFunc, err := newGenericFunc( 241 | "SelectManyByIndexedT", "selectorFn", selectorFn, 242 | simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(Query))), 243 | ) 244 | if err != nil { 245 | panic(err) 246 | } 247 | 248 | selectorFunc := func(index int, outer interface{}) Query { 249 | return selectorGenericFunc.Call(index, outer).(Query) 250 | } 251 | 252 | resultSelectorGenericFunc, err := newGenericFunc( 253 | "SelectManyByIndexedT", "resultSelectorFn", resultSelectorFn, 254 | simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), 255 | ) 256 | if err != nil { 257 | panic(err) 258 | } 259 | 260 | resultSelectorFunc := func(outer interface{}, item interface{}) interface{} { 261 | return resultSelectorGenericFunc.Call(outer, item) 262 | } 263 | 264 | return q.SelectManyByIndexed(selectorFunc, resultSelectorFunc) 265 | } 266 | -------------------------------------------------------------------------------- /selectmany_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | ) 7 | 8 | func TestSelectMany(t *testing.T) { 9 | tests := []struct { 10 | input interface{} 11 | selector func(interface{}) Query 12 | output []interface{} 13 | }{ 14 | {[][]int{{1, 2, 3}, {4, 5, 6, 7}}, func(i interface{}) Query { 15 | return From(i) 16 | }, []interface{}{1, 2, 3, 4, 5, 6, 7}}, 17 | {[]string{"str", "ing"}, func(i interface{}) Query { 18 | return FromString(i.(string)) 19 | }, []interface{}{'s', 't', 'r', 'i', 'n', 'g'}}, 20 | } 21 | 22 | for _, test := range tests { 23 | if q := From(test.input).SelectMany(test.selector); !validateQuery(q, test.output) { 24 | t.Errorf("From(%v).SelectMany()=%v expected %v", test.input, toSlice(q), test.output) 25 | } 26 | } 27 | } 28 | 29 | func TestSelectManyT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 30 | mustPanicWithError(t, "SelectManyT: parameter [selectorFn] has a invalid function signature. Expected: 'func(T)linq.Query', actual: 'func(int)int'", func() { 31 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).SelectManyT(func(item int) int { return item + 2 }) 32 | }) 33 | } 34 | 35 | func TestSelectManyIndexed(t *testing.T) { 36 | tests := []struct { 37 | input interface{} 38 | selector func(int, interface{}) Query 39 | output []interface{} 40 | }{ 41 | {[][]int{{1, 2, 3}, {4, 5, 6, 7}}, func(i int, x interface{}) Query { 42 | if i > 0 { 43 | return From(x.([]int)[1:]) 44 | } 45 | return From(x) 46 | }, []interface{}{1, 2, 3, 5, 6, 7}}, 47 | {[]string{"str", "ing"}, func(i int, x interface{}) Query { 48 | return FromString(x.(string) + strconv.Itoa(i)) 49 | }, []interface{}{'s', 't', 'r', '0', 'i', 'n', 'g', '1'}}, 50 | } 51 | 52 | for _, test := range tests { 53 | if q := From(test.input).SelectManyIndexed(test.selector); !validateQuery(q, test.output) { 54 | t.Errorf("From(%v).SelectManyIndexed()=%v expected %v", test.input, toSlice(q), test.output) 55 | } 56 | } 57 | } 58 | 59 | func TestSelectManyIndexedT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 60 | mustPanicWithError(t, "SelectManyIndexedT: parameter [selectorFn] has a invalid function signature. Expected: 'func(int,T)linq.Query', actual: 'func(int)int'", func() { 61 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).SelectManyIndexedT(func(item int) int { return item + 2 }) 62 | }) 63 | } 64 | 65 | func TestSelectManyBy(t *testing.T) { 66 | tests := []struct { 67 | input interface{} 68 | selector func(interface{}) Query 69 | resultSelector func(interface{}, interface{}) interface{} 70 | output []interface{} 71 | }{ 72 | {[][]int{{1, 2, 3}, {4, 5, 6, 7}}, func(i interface{}) Query { 73 | return From(i) 74 | }, func(x interface{}, y interface{}) interface{} { 75 | return x.(int) + 1 76 | }, []interface{}{2, 3, 4, 5, 6, 7, 8}}, 77 | {[]string{"str", "ing"}, func(i interface{}) Query { 78 | return FromString(i.(string)) 79 | }, func(x interface{}, y interface{}) interface{} { 80 | return string(x.(rune)) + "_" 81 | }, []interface{}{"s_", "t_", "r_", "i_", "n_", "g_"}}, 82 | } 83 | 84 | for _, test := range tests { 85 | if q := From(test.input).SelectManyBy(test.selector, test.resultSelector); !validateQuery(q, test.output) { 86 | t.Errorf("From(%v).SelectManyBy()=%v expected %v", test.input, toSlice(q), test.output) 87 | } 88 | } 89 | } 90 | 91 | func TestSelectManyByT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 92 | mustPanicWithError(t, "SelectManyByT: parameter [selectorFn] has a invalid function signature. Expected: 'func(T)linq.Query', actual: 'func(int)interface {}'", func() { 93 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).SelectManyByT(func(item int) interface{} { return item + 2 }, 2) 94 | }) 95 | } 96 | 97 | func TestSelectManyByT_PanicWhenResultSelectorFnIsInvalid(t *testing.T) { 98 | mustPanicWithError(t, "SelectManyByT: parameter [resultSelectorFn] has a invalid function signature. Expected: 'func(T,T)T', actual: 'func()'", func() { 99 | From([][]int{{1, 1, 1, 2}, {1, 2, 3, 4, 2}}).SelectManyByT( 100 | func(item interface{}) Query { return From(item) }, 101 | func() {}, 102 | ) 103 | }) 104 | } 105 | 106 | func TestSelectManyIndexedBy(t *testing.T) { 107 | tests := []struct { 108 | input interface{} 109 | selector func(int, interface{}) Query 110 | resultSelector func(interface{}, interface{}) interface{} 111 | output []interface{} 112 | }{ 113 | {[][]int{{1, 2, 3}, {4, 5, 6, 7}}, func(i int, x interface{}) Query { 114 | if i == 0 { 115 | return From([]int{10, 20, 30}) 116 | } 117 | return From(x) 118 | }, func(x interface{}, y interface{}) interface{} { 119 | return x.(int) + 1 120 | }, []interface{}{11, 21, 31, 5, 6, 7, 8}}, 121 | {[]string{"st", "ng"}, func(i int, x interface{}) Query { 122 | if i == 0 { 123 | return FromString(x.(string) + "r") 124 | } 125 | return FromString("i" + x.(string)) 126 | }, func(x interface{}, y interface{}) interface{} { 127 | return string(x.(rune)) + "_" 128 | }, []interface{}{"s_", "t_", "r_", "i_", "n_", "g_"}}, 129 | } 130 | 131 | for _, test := range tests { 132 | if q := From(test.input).SelectManyByIndexed(test.selector, test.resultSelector); !validateQuery(q, test.output) { 133 | t.Errorf("From(%v).SelectManyIndexedBy()=%v expected %v", test.input, toSlice(q), test.output) 134 | } 135 | } 136 | } 137 | 138 | func TestSelectManyIndexedByT_PanicWhenSelectorFnIsInvalid(t *testing.T) { 139 | mustPanicWithError(t, "SelectManyByIndexedT: parameter [selectorFn] has a invalid function signature. Expected: 'func(int,T)linq.Query', actual: 'func(int)interface {}'", func() { 140 | From([][]int{{1, 1, 1, 2}, {1, 2, 3, 4, 2}}).SelectManyByIndexedT( 141 | func(item int) interface{} { return item + 2 }, 142 | 2, 143 | ) 144 | }) 145 | } 146 | 147 | func TestSelectManyIndexedByT_PanicWhenResultSelectorFnIsInvalid(t *testing.T) { 148 | mustPanicWithError(t, "SelectManyByIndexedT: parameter [resultSelectorFn] has a invalid function signature. Expected: 'func(T,T)T', actual: 'func()'", func() { 149 | From([][]int{{1, 1, 1, 2}, {1, 2, 3, 4, 2}}).SelectManyByIndexedT( 150 | func(index int, item interface{}) Query { return From(item) }, 151 | func() {}, 152 | ) 153 | }) 154 | } 155 | -------------------------------------------------------------------------------- /setup_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | import "fmt" 6 | 7 | type foo struct { 8 | f1 int 9 | f2 bool 10 | f3 string 11 | } 12 | 13 | func (f foo) Iterate() Iterator { 14 | i := 0 15 | 16 | return func() (item interface{}, ok bool) { 17 | switch i { 18 | case 0: 19 | item = f.f1 20 | ok = true 21 | case 1: 22 | item = f.f2 23 | ok = true 24 | case 2: 25 | item = f.f3 26 | ok = true 27 | default: 28 | ok = false 29 | } 30 | 31 | i++ 32 | return 33 | } 34 | } 35 | 36 | func (f foo) CompareTo(c Comparable) int { 37 | a, b := f.f1, c.(foo).f1 38 | 39 | if a < b { 40 | return -1 41 | } else if a > b { 42 | return 1 43 | } 44 | 45 | return 0 46 | } 47 | 48 | func toSlice(q Query) (result []interface{}) { 49 | next := q.Iterate() 50 | 51 | for item, ok := next(); ok; item, ok = next() { 52 | result = append(result, item) 53 | } 54 | 55 | return 56 | } 57 | 58 | func validateQuery(q Query, output []interface{}) bool { 59 | next := q.Iterate() 60 | 61 | for _, oitem := range output { 62 | qitem, _ := next() 63 | 64 | if oitem != qitem { 65 | return false 66 | } 67 | } 68 | 69 | _, ok := next() 70 | _, ok2 := next() 71 | return !(ok || ok2) 72 | } 73 | 74 | func mustPanicWithError(t *testing.T, expectedErr string, f func()) { 75 | defer func() { 76 | r := recover() 77 | err := fmt.Sprintf("%s", r) 78 | if err != expectedErr { 79 | t.Fatalf("got=[%v] expected=[%v]", err, expectedErr) 80 | } 81 | }() 82 | f() 83 | } 84 | -------------------------------------------------------------------------------- /skip.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Skip bypasses a specified number of elements in a collection and then returns 4 | // the remaining elements. 5 | func (q Query) Skip(count int) Query { 6 | return Query{ 7 | Iterate: func() Iterator { 8 | next := q.Iterate() 9 | n := count 10 | 11 | return func() (item interface{}, ok bool) { 12 | for ; n > 0; n-- { 13 | item, ok = next() 14 | if !ok { 15 | return 16 | } 17 | } 18 | 19 | return next() 20 | } 21 | }, 22 | } 23 | } 24 | 25 | // SkipWhile bypasses elements in a collection as long as a specified condition 26 | // is true and then returns the remaining elements. 27 | // 28 | // This method tests each element by using predicate and skips the element if 29 | // the result is true. After the predicate function returns false for an 30 | // element, that element and the remaining elements in source are returned and 31 | // there are no more invocations of predicate. 32 | func (q Query) SkipWhile(predicate func(interface{}) bool) Query { 33 | return Query{ 34 | Iterate: func() Iterator { 35 | next := q.Iterate() 36 | ready := false 37 | 38 | return func() (item interface{}, ok bool) { 39 | for !ready { 40 | item, ok = next() 41 | if !ok { 42 | return 43 | } 44 | 45 | ready = !predicate(item) 46 | if ready { 47 | return 48 | } 49 | } 50 | 51 | return next() 52 | } 53 | }, 54 | } 55 | } 56 | 57 | // SkipWhileT is the typed version of SkipWhile. 58 | // 59 | // - predicateFn is of type "func(TSource)bool" 60 | // 61 | // NOTE: SkipWhile has better performance than SkipWhileT. 62 | func (q Query) SkipWhileT(predicateFn interface{}) Query { 63 | 64 | predicateGenericFunc, err := newGenericFunc( 65 | "SkipWhileT", "predicateFn", predicateFn, 66 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 67 | ) 68 | if err != nil { 69 | panic(err) 70 | } 71 | 72 | predicateFunc := func(item interface{}) bool { 73 | return predicateGenericFunc.Call(item).(bool) 74 | } 75 | 76 | return q.SkipWhile(predicateFunc) 77 | } 78 | 79 | // SkipWhileIndexed bypasses elements in a collection as long as a specified 80 | // condition is true and then returns the remaining elements. The element's 81 | // index is used in the logic of the predicate function. 82 | // 83 | // This method tests each element by using predicate and skips the element if 84 | // the result is true. After the predicate function returns false for an 85 | // element, that element and the remaining elements in source are returned and 86 | // there are no more invocations of predicate. 87 | func (q Query) SkipWhileIndexed(predicate func(int, interface{}) bool) Query { 88 | return Query{ 89 | Iterate: func() Iterator { 90 | next := q.Iterate() 91 | ready := false 92 | index := 0 93 | 94 | return func() (item interface{}, ok bool) { 95 | for !ready { 96 | item, ok = next() 97 | if !ok { 98 | return 99 | } 100 | 101 | ready = !predicate(index, item) 102 | if ready { 103 | return 104 | } 105 | 106 | index++ 107 | } 108 | 109 | return next() 110 | } 111 | }, 112 | } 113 | } 114 | 115 | // SkipWhileIndexedT is the typed version of SkipWhileIndexed. 116 | // 117 | // - predicateFn is of type "func(int,TSource)bool" 118 | // 119 | // NOTE: SkipWhileIndexed has better performance than SkipWhileIndexedT. 120 | func (q Query) SkipWhileIndexedT(predicateFn interface{}) Query { 121 | predicateGenericFunc, err := newGenericFunc( 122 | "SkipWhileIndexedT", "predicateFn", predicateFn, 123 | simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))), 124 | ) 125 | if err != nil { 126 | panic(err) 127 | } 128 | 129 | predicateFunc := func(index int, item interface{}) bool { 130 | return predicateGenericFunc.Call(index, item).(bool) 131 | } 132 | 133 | return q.SkipWhileIndexed(predicateFunc) 134 | } 135 | -------------------------------------------------------------------------------- /skip_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestSkip(t *testing.T) { 6 | tests := []struct { 7 | input interface{} 8 | output []interface{} 9 | }{ 10 | {[]int{1, 2}, []interface{}{}}, 11 | {[]int{1, 2, 2, 3, 1}, []interface{}{3, 1}}, 12 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, []interface{}{2, 1, 2, 3, 4, 2}}, 13 | {"sstr", []interface{}{'r'}}, 14 | } 15 | 16 | for _, test := range tests { 17 | if q := From(test.input).Skip(3); !validateQuery(q, test.output) { 18 | t.Errorf("From(%v).Skip(3)=%v expected %v", test.input, toSlice(q), test.output) 19 | } 20 | } 21 | } 22 | 23 | func TestSkipWhile(t *testing.T) { 24 | tests := []struct { 25 | input interface{} 26 | predicate func(interface{}) bool 27 | output []interface{} 28 | }{ 29 | {[]int{1, 2}, func(i interface{}) bool { 30 | return i.(int) < 3 31 | }, []interface{}{}}, 32 | {[]int{4, 1, 2}, func(i interface{}) bool { 33 | return i.(int) < 3 34 | }, []interface{}{4, 1, 2}}, 35 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, func(i interface{}) bool { 36 | return i.(int) < 3 37 | }, []interface{}{3, 4, 2}}, 38 | {"sstr", func(i interface{}) bool { 39 | return i.(rune) == 's' 40 | }, []interface{}{'t', 'r'}}, 41 | } 42 | 43 | for _, test := range tests { 44 | if q := From(test.input).SkipWhile(test.predicate); !validateQuery(q, test.output) { 45 | t.Errorf("From(%v).SkipWhile()=%v expected %v", test.input, toSlice(q), test.output) 46 | } 47 | } 48 | } 49 | 50 | func TestSkipWhileT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 51 | mustPanicWithError(t, "SkipWhileT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int,int)bool'", func() { 52 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).SkipWhileT(func(item int, x int) bool { return item == 1 }) 53 | }) 54 | } 55 | 56 | func TestSkipWhileIndexed(t *testing.T) { 57 | tests := []struct { 58 | input interface{} 59 | predicate func(int, interface{}) bool 60 | output []interface{} 61 | }{ 62 | {[]int{1, 2}, func(i int, x interface{}) bool { 63 | return x.(int) < 3 64 | }, []interface{}{}}, 65 | {[]int{4, 1, 2}, func(i int, x interface{}) bool { 66 | return x.(int) < 3 67 | }, []interface{}{4, 1, 2}}, 68 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, func(i int, x interface{}) bool { 69 | return x.(int) < 2 || i < 5 70 | }, []interface{}{2, 3, 4, 2}}, 71 | {"sstr", func(i int, x interface{}) bool { 72 | return x.(rune) == 's' && i < 1 73 | }, []interface{}{'s', 't', 'r'}}, 74 | } 75 | 76 | for _, test := range tests { 77 | if q := From(test.input).SkipWhileIndexed(test.predicate); !validateQuery(q, test.output) { 78 | t.Errorf("From(%v).SkipWhileIndexed()=%v expected %v", test.input, toSlice(q), test.output) 79 | } 80 | } 81 | } 82 | 83 | func TestSkipWhileIndexedT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 84 | mustPanicWithError(t, "SkipWhileIndexedT: parameter [predicateFn] has a invalid function signature. Expected: 'func(int,T)bool', actual: 'func(int,int,int)bool'", func() { 85 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).SkipWhileIndexedT(func(item int, x int, y int) bool { return item == 1 }) 86 | }) 87 | } 88 | -------------------------------------------------------------------------------- /take.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Take returns a specified number of contiguous elements from the start of a 4 | // collection. 5 | func (q Query) Take(count int) Query { 6 | return Query{ 7 | Iterate: func() Iterator { 8 | next := q.Iterate() 9 | n := count 10 | 11 | return func() (item interface{}, ok bool) { 12 | if n <= 0 { 13 | return 14 | } 15 | 16 | n-- 17 | return next() 18 | } 19 | }, 20 | } 21 | } 22 | 23 | // TakeWhile returns elements from a collection as long as a specified condition 24 | // is true, and then skips the remaining elements. 25 | func (q Query) TakeWhile(predicate func(interface{}) bool) Query { 26 | return Query{ 27 | Iterate: func() Iterator { 28 | next := q.Iterate() 29 | done := false 30 | 31 | return func() (item interface{}, ok bool) { 32 | if done { 33 | return 34 | } 35 | 36 | item, ok = next() 37 | if !ok { 38 | done = true 39 | return 40 | } 41 | 42 | if predicate(item) { 43 | return 44 | } 45 | 46 | done = true 47 | return nil, false 48 | } 49 | }, 50 | } 51 | } 52 | 53 | // TakeWhileT is the typed version of TakeWhile. 54 | // 55 | // - predicateFn is of type "func(TSource)bool" 56 | // 57 | // NOTE: TakeWhile has better performance than TakeWhileT. 58 | func (q Query) TakeWhileT(predicateFn interface{}) Query { 59 | 60 | predicateGenericFunc, err := newGenericFunc( 61 | "TakeWhileT", "predicateFn", predicateFn, 62 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 63 | ) 64 | if err != nil { 65 | panic(err) 66 | } 67 | 68 | predicateFunc := func(item interface{}) bool { 69 | return predicateGenericFunc.Call(item).(bool) 70 | } 71 | 72 | return q.TakeWhile(predicateFunc) 73 | } 74 | 75 | // TakeWhileIndexed returns elements from a collection as long as a specified 76 | // condition is true. The element's index is used in the logic of the predicate 77 | // function. The first argument of predicate represents the zero-based index of 78 | // the element within collection. The second argument represents the element to 79 | // test. 80 | func (q Query) TakeWhileIndexed(predicate func(int, interface{}) bool) Query { 81 | return Query{ 82 | Iterate: func() Iterator { 83 | next := q.Iterate() 84 | done := false 85 | index := 0 86 | 87 | return func() (item interface{}, ok bool) { 88 | if done { 89 | return 90 | } 91 | 92 | item, ok = next() 93 | if !ok { 94 | done = true 95 | return 96 | } 97 | 98 | if predicate(index, item) { 99 | index++ 100 | return 101 | } 102 | 103 | done = true 104 | return nil, false 105 | } 106 | }, 107 | } 108 | } 109 | 110 | // TakeWhileIndexedT is the typed version of TakeWhileIndexed. 111 | // 112 | // - predicateFn is of type "func(int,TSource)bool" 113 | // 114 | // NOTE: TakeWhileIndexed has better performance than TakeWhileIndexedT. 115 | func (q Query) TakeWhileIndexedT(predicateFn interface{}) Query { 116 | whereFunc, err := newGenericFunc( 117 | "TakeWhileIndexedT", "predicateFn", predicateFn, 118 | simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))), 119 | ) 120 | if err != nil { 121 | panic(err) 122 | } 123 | 124 | predicateFunc := func(index int, item interface{}) bool { 125 | return whereFunc.Call(index, item).(bool) 126 | } 127 | 128 | return q.TakeWhileIndexed(predicateFunc) 129 | } 130 | -------------------------------------------------------------------------------- /take_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestTake(t *testing.T) { 6 | tests := []struct { 7 | input interface{} 8 | output []interface{} 9 | }{ 10 | {[]int{1, 2, 2, 3, 1}, []interface{}{1, 2, 2}}, 11 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, []interface{}{1, 1, 1}}, 12 | {"sstr", []interface{}{'s', 's', 't'}}, 13 | } 14 | 15 | for _, test := range tests { 16 | if q := From(test.input).Take(3); !validateQuery(q, test.output) { 17 | t.Errorf("From(%v).Take(3)=%v expected %v", test.input, toSlice(q), test.output) 18 | } 19 | } 20 | } 21 | 22 | func TestTakeWhile(t *testing.T) { 23 | tests := []struct { 24 | input interface{} 25 | predicate func(interface{}) bool 26 | output []interface{} 27 | }{ 28 | {[]int{1, 1, 1, 2, 1, 2}, func(i interface{}) bool { 29 | return i.(int) < 3 30 | }, []interface{}{1, 1, 1, 2, 1, 2}}, 31 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, func(i interface{}) bool { 32 | return i.(int) < 3 33 | }, []interface{}{1, 1, 1, 2, 1, 2}}, 34 | {"sstr", func(i interface{}) bool { 35 | return i.(rune) == 's' 36 | }, []interface{}{'s', 's'}}, 37 | } 38 | 39 | for _, test := range tests { 40 | if q := From(test.input).TakeWhile(test.predicate); !validateQuery(q, test.output) { 41 | t.Errorf("From(%v).TakeWhile()=%v expected %v", test.input, toSlice(q), test.output) 42 | } 43 | } 44 | } 45 | 46 | func TestTakeWhileT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 47 | mustPanicWithError(t, "TakeWhileT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int)int'", func() { 48 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).TakeWhileT(func(item int) int { return item + 2 }) 49 | }) 50 | } 51 | 52 | func TestTakeWhileIndexed(t *testing.T) { 53 | tests := []struct { 54 | input interface{} 55 | predicate func(int, interface{}) bool 56 | output []interface{} 57 | }{ 58 | {[]int{1, 1, 1, 2}, func(i int, x interface{}) bool { 59 | return x.(int) < 2 || i < 5 60 | }, []interface{}{1, 1, 1, 2}}, 61 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, func(i int, x interface{}) bool { 62 | return x.(int) < 2 || i < 5 63 | }, []interface{}{1, 1, 1, 2, 1}}, 64 | {"sstr", func(i int, x interface{}) bool { 65 | return x.(rune) == 's' && i < 1 66 | }, []interface{}{'s'}}, 67 | } 68 | 69 | for _, test := range tests { 70 | if q := From(test.input).TakeWhileIndexed(test.predicate); !validateQuery(q, test.output) { 71 | t.Errorf("From(%v).TakeWhileIndexed()=%v expected %v", test.input, toSlice(q), test.output) 72 | } 73 | } 74 | } 75 | 76 | func TestTakeWhileIndexedT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 77 | mustPanicWithError(t, "TakeWhileIndexedT: parameter [predicateFn] has a invalid function signature. Expected: 'func(int,T)bool', actual: 'func(int)int'", func() { 78 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).TakeWhileIndexedT(func(item int) int { return item + 2 }) 79 | }) 80 | } 81 | -------------------------------------------------------------------------------- /union.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Union produces the set union of two collections. 4 | // 5 | // This method excludes duplicates from the return set. This is different 6 | // behavior to the Concat method, which returns all the elements in the input 7 | // collection including duplicates. 8 | func (q Query) Union(q2 Query) Query { 9 | return Query{ 10 | Iterate: func() Iterator { 11 | next := q.Iterate() 12 | next2 := q2.Iterate() 13 | 14 | set := make(map[interface{}]bool) 15 | use1 := true 16 | 17 | return func() (item interface{}, ok bool) { 18 | if use1 { 19 | for item, ok = next(); ok; item, ok = next() { 20 | if _, has := set[item]; !has { 21 | set[item] = true 22 | return 23 | } 24 | } 25 | 26 | use1 = false 27 | } 28 | 29 | for item, ok = next2(); ok; item, ok = next2() { 30 | if _, has := set[item]; !has { 31 | set[item] = true 32 | return 33 | } 34 | } 35 | 36 | return 37 | } 38 | }, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /union_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestUnion(t *testing.T) { 6 | input1 := []int{1, 2, 3} 7 | input2 := []int{2, 4, 5, 1} 8 | want := []interface{}{1, 2, 3, 4, 5} 9 | 10 | if q := From(input1).Union(From(input2)); !validateQuery(q, want) { 11 | t.Errorf("From(%v).Union(%v)=%v expected %v", input1, input2, toSlice(q), want) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /where.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Where filters a collection of values based on a predicate. 4 | func (q Query) Where(predicate func(interface{}) bool) Query { 5 | return Query{ 6 | Iterate: func() Iterator { 7 | next := q.Iterate() 8 | 9 | return func() (item interface{}, ok bool) { 10 | for item, ok = next(); ok; item, ok = next() { 11 | if predicate(item) { 12 | return 13 | } 14 | } 15 | 16 | return 17 | } 18 | }, 19 | } 20 | } 21 | 22 | // WhereT is the typed version of Where. 23 | // 24 | // - predicateFn is of type "func(TSource)bool" 25 | // 26 | // NOTE: Where has better performance than WhereT. 27 | func (q Query) WhereT(predicateFn interface{}) Query { 28 | 29 | predicateGenericFunc, err := newGenericFunc( 30 | "WhereT", "predicateFn", predicateFn, 31 | simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))), 32 | ) 33 | if err != nil { 34 | panic(err) 35 | } 36 | 37 | predicateFunc := func(item interface{}) bool { 38 | return predicateGenericFunc.Call(item).(bool) 39 | } 40 | 41 | return q.Where(predicateFunc) 42 | } 43 | 44 | // WhereIndexed filters a collection of values based on a predicate. Each 45 | // element's index is used in the logic of the predicate function. 46 | // 47 | // The first argument represents the zero-based index of the element within 48 | // collection. The second argument of predicate represents the element to test. 49 | func (q Query) WhereIndexed(predicate func(int, interface{}) bool) Query { 50 | return Query{ 51 | Iterate: func() Iterator { 52 | next := q.Iterate() 53 | index := 0 54 | 55 | return func() (item interface{}, ok bool) { 56 | for item, ok = next(); ok; item, ok = next() { 57 | if predicate(index, item) { 58 | index++ 59 | return 60 | } 61 | 62 | index++ 63 | } 64 | 65 | return 66 | } 67 | }, 68 | } 69 | } 70 | 71 | // WhereIndexedT is the typed version of WhereIndexed. 72 | // 73 | // - predicateFn is of type "func(int,TSource)bool" 74 | // 75 | // NOTE: WhereIndexed has better performance than WhereIndexedT. 76 | func (q Query) WhereIndexedT(predicateFn interface{}) Query { 77 | predicateGenericFunc, err := newGenericFunc( 78 | "WhereIndexedT", "predicateFn", predicateFn, 79 | simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))), 80 | ) 81 | if err != nil { 82 | panic(err) 83 | } 84 | 85 | predicateFunc := func(index int, item interface{}) bool { 86 | return predicateGenericFunc.Call(index, item).(bool) 87 | } 88 | 89 | return q.WhereIndexed(predicateFunc) 90 | } 91 | -------------------------------------------------------------------------------- /where_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestWhere(t *testing.T) { 6 | tests := []struct { 7 | input interface{} 8 | predicate func(interface{}) bool 9 | output []interface{} 10 | }{ 11 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, func(i interface{}) bool { 12 | return i.(int) >= 3 13 | }, []interface{}{3, 4}}, 14 | {"sstr", func(i interface{}) bool { 15 | return i.(rune) != 's' 16 | }, []interface{}{'t', 'r'}}, 17 | } 18 | 19 | for _, test := range tests { 20 | if q := From(test.input).Where(test.predicate); !validateQuery(q, test.output) { 21 | t.Errorf("From(%v).Where()=%v expected %v", test.input, toSlice(q), test.output) 22 | } 23 | } 24 | } 25 | 26 | func TestWhereT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 27 | mustPanicWithError(t, "WhereT: parameter [predicateFn] has a invalid function signature. Expected: 'func(T)bool', actual: 'func(int)int'", func() { 28 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).WhereT(func(item int) int { return item + 2 }) 29 | }) 30 | } 31 | 32 | func TestWhereIndexed(t *testing.T) { 33 | tests := []struct { 34 | input interface{} 35 | predicate func(int, interface{}) bool 36 | output []interface{} 37 | }{ 38 | {[9]int{1, 1, 1, 2, 1, 2, 3, 4, 2}, func(i int, x interface{}) bool { 39 | return x.(int) < 4 && i > 4 40 | }, []interface{}{2, 3, 2}}, 41 | {"sstr", func(i int, x interface{}) bool { 42 | return x.(rune) != 's' || i == 1 43 | }, []interface{}{'s', 't', 'r'}}, 44 | {"abcde", func(i int, _ interface{}) bool { 45 | return i < 2 46 | }, []interface{}{'a', 'b'}}, 47 | } 48 | 49 | for _, test := range tests { 50 | if q := From(test.input).WhereIndexed(test.predicate); !validateQuery(q, test.output) { 51 | t.Errorf("From(%v).WhereIndexed()=%v expected %v", test.input, toSlice(q), test.output) 52 | } 53 | } 54 | } 55 | 56 | func TestWhereIndexedT_PanicWhenPredicateFnIsInvalid(t *testing.T) { 57 | mustPanicWithError(t, "WhereIndexedT: parameter [predicateFn] has a invalid function signature. Expected: 'func(int,T)bool', actual: 'func(string)'", func() { 58 | From([]int{1, 1, 1, 2, 1, 2, 3, 4, 2}).WhereIndexedT(func(item string) {}) 59 | }) 60 | } 61 | -------------------------------------------------------------------------------- /zip.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | // Zip applies a specified function to the corresponding elements of two 4 | // collections, producing a collection of the results. 5 | // 6 | // The method steps through the two input collections, applying function 7 | // resultSelector to corresponding elements of the two collections. The method 8 | // returns a collection of the values that are returned by resultSelector. If 9 | // the input collections do not have the same number of elements, the method 10 | // combines elements until it reaches the end of one of the collections. For 11 | // example, if one collection has three elements and the other one has four, the 12 | // result collection has only three elements. 13 | func (q Query) Zip(q2 Query, 14 | resultSelector func(interface{}, interface{}) interface{}) Query { 15 | 16 | return Query{ 17 | Iterate: func() Iterator { 18 | next1 := q.Iterate() 19 | next2 := q2.Iterate() 20 | 21 | return func() (item interface{}, ok bool) { 22 | item1, ok1 := next1() 23 | item2, ok2 := next2() 24 | 25 | if ok1 && ok2 { 26 | return resultSelector(item1, item2), true 27 | } 28 | 29 | return nil, false 30 | } 31 | }, 32 | } 33 | } 34 | 35 | // ZipT is the typed version of Zip. 36 | // 37 | // - resultSelectorFn is of type "func(TFirst,TSecond)TResult" 38 | // 39 | // NOTE: Zip has better performance than ZipT. 40 | func (q Query) ZipT(q2 Query, 41 | resultSelectorFn interface{}) Query { 42 | resultSelectorGenericFunc, err := newGenericFunc( 43 | "ZipT", "resultSelectorFn", resultSelectorFn, 44 | simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))), 45 | ) 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | resultSelectorFunc := func(item1 interface{}, item2 interface{}) interface{} { 51 | return resultSelectorGenericFunc.Call(item1, item2) 52 | } 53 | 54 | return q.Zip(q2, resultSelectorFunc) 55 | } 56 | -------------------------------------------------------------------------------- /zip_test.go: -------------------------------------------------------------------------------- 1 | package linq 2 | 3 | import "testing" 4 | 5 | func TestZip(t *testing.T) { 6 | input1 := []int{1, 2, 3} 7 | input2 := []int{2, 4, 5, 1} 8 | want := []interface{}{3, 6, 8} 9 | 10 | if q := From(input1).Zip(From(input2), func(i, j interface{}) interface{} { 11 | return i.(int) + j.(int) 12 | }); !validateQuery(q, want) { 13 | t.Errorf("From(%v).Zip(%v)=%v expected %v", input1, input2, toSlice(q), want) 14 | } 15 | } 16 | 17 | func TestZipT_PanicWhenResultSelectorFnIsInvalid(t *testing.T) { 18 | mustPanicWithError(t, "ZipT: parameter [resultSelectorFn] has a invalid function signature. Expected: 'func(T,T)T', actual: 'func(int,int,int)int'", func() { 19 | input1 := []int{1, 2, 3} 20 | input2 := []int{2, 4, 5, 1} 21 | 22 | From(input1).ZipT(From(input2), func(i, j, k int) int { 23 | return i + j 24 | }) 25 | }) 26 | } 27 | --------------------------------------------------------------------------------