feat(Go-Tool):2020/11/12:新增linq包使用示例(未完成)

This commit is contained in:
Huangzj
2020-11-12 18:10:15 +08:00
parent 3616c4b5e5
commit 2ac2b2666f
33 changed files with 2517 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
### Code ###
# Visual Studio Code - https://code.visualstudio.com/
.settings/
.vscode/
+20
View File
@@ -0,0 +1,20 @@
sudo: false
language: go
go:
- 1.5
- 1.7
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
- go get -u github.com/golang/lint/golint
script:
- go vet -x ./...
- golint ./...
- go test -v ./...
- go test -covermode=count -coverprofile=profile.cov
after_script:
- goveralls -coverprofile=profile.cov -service=travis-ci
+14
View File
@@ -0,0 +1,14 @@
Copyright 2016 Ahmet Alp Balkan
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+141
View File
@@ -0,0 +1,141 @@
# go-linq [![GoDoc](https://godoc.org/github.com/ahmetalpbalkan/go-linq?status.svg)](https://godoc.org/github.com/ahmetalpbalkan/go-linq) [![Build Status](https://travis-ci.org/ahmetalpbalkan/go-linq.svg?branch=master)](https://travis-ci.org/ahmetalpbalkan/go-linq) [![Coverage Status](https://coveralls.io/repos/github/ahmetalpbalkan/go-linq/badge.svg?branch=master)](https://coveralls.io/github/ahmetalpbalkan/go-linq?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ahmetalpbalkan/go-linq)](https://goreportcard.com/report/github.com/ahmetalpbalkan/go-linq)
A powerful language integrated query (LINQ) library for Go.
* Written in vanilla Go!
* Safe for concurrent use
* Complete lazy evaluation with iterator pattern
* Supports arrays, slices, maps, strings, channels and custom collections
(collection needs to implement `Iterable` interface and element - `Comparable`
interface)
## Installation
$ go get github.com/ahmetalpbalkan/go-linq
> :warning: :warning: `go-linq` has recently introduced _breaking API changes_
> with v2.0.0. See [release notes](#release-notes) for details. v2.0.0 comes with
> a refined interface, dramatically increased performance and memory efficiency,
> and new features such as lazy evaluation ([read more](http://kalan.rocks/2016/07/16/manipulating-data-with-iterators-in-go/)).
>
> The old version is still available in `archive/0.9` branch and tagged as `0.9`
> as well. If you are using `go-linq`, please vendor a copy of it in your
> source tree to avoid getting broken by upstream changes.
## Quickstart
Usage is as easy as chaining methods like:
`From(slice)` `.Where(predicate)` `.Select(selector)` `.Union(data)`
**Example: Find all owners of cars manufactured from 2015**
```go
import . "github.com/ahmetalpbalkan/go-linq"
type Car struct {
id, year int
owner, model string
}
owners := []string{}
From(cars).Where(func(c interface{}) bool {
return c.(Car).year >= 2015
}).Select(func(c interface{}) interface{} {
return c.(Car).owner
}).ToSlice(&owners)
```
**Example: Find the author who has written the most books**
```go
import . "github.com/ahmetalpbalkan/go-linq"
type Book struct {
id int
title string
authors []string
}
author := From(books).SelectMany( // make a flat array of authors
func(book interface{}) Query {
return From(book.(Book).authors)
}).GroupBy( // group by author
func(author interface{}) interface{} {
return author // author as key
}, func(author interface{}) interface{} {
return author // author as value
}).OrderByDescending( // sort groups by its length
func(group interface{}) interface{} {
return len(group.(Group).Group)
}).Select( // get authors out of groups
func(group interface{}) interface{} {
return group.(Group).Key
}).First() // take the first author
```
**Example: Implement a custom method that leaves only values greater than the specified threshold**
```go
type MyQuery Query
func (q MyQuery) GreaterThan(threshold int) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
return func() (item interface{}, ok bool) {
for item, ok = next(); ok; item, ok = next() {
if item.(int) > threshold {
return
}
}
return
}
},
}
}
result := MyQuery(Range(1,10)).GreaterThan(5).Results()
```
**More examples** can be found in [documentation](https://godoc.org/github.com/ahmetalpbalkan/go-linq).
## Release Notes
~~~
v2.0.0 (2016-09-02)
* IMPORTANT: This release is a BREAKING CHANGE. The old version
is archived at the 'archive/0.9' branch or the 0.9 tags.
* A COMPLETE REWRITE of go-linq with better performance and memory
efficiency. (thanks @kalaninja!)
* API has significantly changed. Most notably:
- linq.T removed in favor of interface{}
- library methods no longer return errors
- PLINQ removed for now (see channels support)
- support for channels, custom collections and comparables
v0.9-rc4
* GroupBy()
v0.9-rc3.2
* bugfix: All() iterating over values instead of indices
v0.9-rc3.1
* bugfix: modifying result slice affects subsequent query methods
v0.9-rc3
* removed FirstOrNil, LastOrNil, ElementAtOrNil methods
v0.9-rc2.5
* slice-accepting methods accept slices of any type with reflections
v0.9-rc2
* parallel linq (plinq) implemented
* Queryable separated into Query & ParallelQuery
* fixed early termination for All
v0.9-rc1
* many linq methods are implemented
* methods have error handling support
* type assertion limitations are unresolved
* travis-ci.org build integrated
* open sourced on github, master & dev branches
~~~
+53
View File
@@ -0,0 +1,53 @@
package linq
// Aggregate applies an accumulator function over a sequence.
//
// Aggregate method makes it simple to perform a calculation over a sequence of values.
// This method works by calling f() one time for each element in source
// except the first one. Each time f() is called, Aggregate passes both
// the element from the sequence and an aggregated value (as the first argument to f()).
// The first element of source is used as the initial aggregate value.
// The result of f() replaces the previous aggregated value.
//
// Aggregate returns the final result of f().
func (q Query) Aggregate(
f func(interface{}, interface{}) interface{}) interface{} {
next := q.Iterate()
result, any := next()
if !any {
return nil
}
for current, ok := next(); ok; current, ok = next() {
result = f(result, current)
}
return result
}
// AggregateWithSeed applies an accumulator function over a sequence.
// The specified seed value is used as the initial accumulator value.
//
// Aggregate method makes it simple to perform a calculation over a sequence of values.
// This method works by calling f() one time for each element in source
// except the first one. Each time f() is called, Aggregate passes both
// the element from the sequence and an aggregated value (as the first argument to f()).
// The value of the seed parameter is used as the initial aggregate value.
// The result of f() replaces the previous aggregated value.
//
// Aggregate returns the final result of f().
func (q Query) AggregateWithSeed(
seed interface{},
f func(interface{}, interface{}) interface{},
) interface{} {
next := q.Iterate()
result := seed
for current, ok := next(); ok; current, ok = next() {
result = f(result, current)
}
return result
}
+200
View File
@@ -0,0 +1,200 @@
package linq
type comparer func(interface{}, interface{}) int
// Comparable is an interface that has to be implemented by a
// custom collection elememts in order to work with linq.
//
// Example:
// func (f foo) CompareTo(c Comparable) int {
// a, b := f.f1, c.(foo).f1
//
// if a < b {
// return -1
// } else if a > b {
// return 1
// }
//
// return 0
// }
type Comparable interface {
CompareTo(Comparable) int
}
func getComparer(data interface{}) comparer {
switch data.(type) {
case int:
return func(x, y interface{}) int {
a, b := x.(int), y.(int)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case int8:
return func(x, y interface{}) int {
a, b := x.(int8), y.(int8)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case int16:
return func(x, y interface{}) int {
a, b := x.(int16), y.(int16)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case int32:
return func(x, y interface{}) int {
a, b := x.(int32), y.(int32)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case int64:
return func(x, y interface{}) int {
a, b := x.(int64), y.(int64)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case uint:
return func(x, y interface{}) int {
a, b := x.(uint), y.(uint)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case uint8:
return func(x, y interface{}) int {
a, b := x.(uint8), y.(uint8)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case uint16:
return func(x, y interface{}) int {
a, b := x.(uint16), y.(uint16)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case uint32:
return func(x, y interface{}) int {
a, b := x.(uint32), y.(uint32)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case uint64:
return func(x, y interface{}) int {
a, b := x.(uint64), y.(uint64)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case float32:
return func(x, y interface{}) int {
a, b := x.(float32), y.(float32)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case float64:
return func(x, y interface{}) int {
a, b := x.(float64), y.(float64)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case string:
return func(x, y interface{}) int {
a, b := x.(string), y.(string)
switch {
case a > b:
return 1
case b > a:
return -1
default:
return 0
}
}
case bool:
return func(x, y interface{}) int {
a, b := x.(bool), y.(bool)
switch {
case a == b:
return 0
case a:
return 1
default:
return -1
}
}
default:
return func(x, y interface{}) int {
a, b := x.(Comparable), y.(Comparable)
return a.CompareTo(b)
}
}
}
+74
View File
@@ -0,0 +1,74 @@
package linq
// Append inserts an item to the end of a collection,
// so it becomes the last item.
func (q Query) Append(item interface{}) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
appended := false
return func() (interface{}, bool) {
i, ok := next()
if ok {
return i, ok
}
if !appended {
appended = true
return item, true
}
return nil, false
}
},
}
}
// Concat concatenates two collections.
//
// The Concat method differs from the Union method because the Concat method
// returns all the original elements in the input sequences.
// The Union method returns only unique elements.
func (q Query) Concat(q2 Query) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
next2 := q2.Iterate()
use1 := true
return func() (item interface{}, ok bool) {
if use1 {
item, ok = next()
if ok {
return
}
use1 = false
}
return next2()
}
},
}
}
// Prepend inserts an item to the beginning of a collection,
// so it becomes the first item.
func (q Query) Prepend(item interface{}) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
prepended := false
return func() (interface{}, bool) {
if prepended {
return next()
}
prepended = true
return item, true
}
},
}
}
+70
View File
@@ -0,0 +1,70 @@
package linq
type intConverter func(interface{}) int64
func getIntConverter(data interface{}) intConverter {
switch data.(type) {
case (int):
return func(i interface{}) int64 {
return int64(i.(int))
}
case (int8):
return func(i interface{}) int64 {
return int64(i.(int8))
}
case (int16):
return func(i interface{}) int64 {
return int64(i.(int16))
}
case (int32):
return func(i interface{}) int64 {
return int64(i.(int32))
}
}
return func(i interface{}) int64 {
return i.(int64)
}
}
type uintConverter func(interface{}) uint64
func getUIntConverter(data interface{}) uintConverter {
switch data.(type) {
case (uint):
return func(i interface{}) uint64 {
return uint64(i.(uint))
}
case (uint8):
return func(i interface{}) uint64 {
return uint64(i.(uint8))
}
case (uint16):
return func(i interface{}) uint64 {
return uint64(i.(uint16))
}
case (uint32):
return func(i interface{}) uint64 {
return uint64(i.(uint32))
}
}
return func(i interface{}) uint64 {
return i.(uint64)
}
}
type floatConverter func(interface{}) float64
func getFloatConverter(data interface{}) floatConverter {
switch data.(type) {
case (float32):
return func(i interface{}) float64 {
return float64(i.(float32))
}
}
return func(i interface{}) float64 {
return i.(float64)
}
}
+75
View File
@@ -0,0 +1,75 @@
package linq
// Distinct method returns distinct elements from a collection.
// The result is an unordered collection that contains no duplicate values.
func (q Query) Distinct() Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
set := make(map[interface{}]bool)
return func() (item interface{}, ok bool) {
for item, ok = next(); ok; item, ok = next() {
if _, has := set[item]; !has {
set[item] = true
return
}
}
return
}
},
}
}
// Distinct method returns distinct elements from a collection.
// The result is an ordered collection that contains no duplicate values.
//
// NOTE: Distinct method on OrderedQuery type has better performance than
// Distinct method on Query type
func (oq OrderedQuery) Distinct() OrderedQuery {
return OrderedQuery{
orders: oq.orders,
Query: Query{
Iterate: func() Iterator {
next := oq.Iterate()
var prev interface{}
return func() (item interface{}, ok bool) {
for item, ok = next(); ok; item, ok = next() {
if item != prev {
prev = item
return
}
}
return
}
},
},
}
}
// DistinctBy method returns distinct elements from a collection. This method
// executes selector function for each element to determine a value to compare.
// The result is an unordered collection that contains no duplicate values.
func (q Query) DistinctBy(selector func(interface{}) interface{}) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
set := make(map[interface{}]bool)
return func() (item interface{}, ok bool) {
for item, ok = next(); ok; item, ok = next() {
s := selector(item)
if _, has := set[s]; !has {
set[s] = true
return
}
}
return
}
},
}
}
+5
View File
@@ -0,0 +1,5 @@
// Package linq provides methods for querying and manipulating
// slices, arrays, maps, strings, channels and collections.
//
// Authors: Alexander Kalankhodzhaev (kalan), Ahmet Alp Balkan
package linq
+59
View File
@@ -0,0 +1,59 @@
package linq
// Except produces the set difference of two sequences.
// The set difference is the members of the first sequence
// that don't appear in the second sequence.
func (q Query) Except(q2 Query) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
next2 := q2.Iterate()
set := make(map[interface{}]bool)
for i, ok := next2(); ok; i, ok = next2() {
set[i] = true
}
return func() (item interface{}, ok bool) {
for item, ok = next(); ok; item, ok = next() {
if _, has := set[item]; !has {
return
}
}
return
}
},
}
}
// ExceptBy invokes a transform function on each element of a collection
// and produces the set difference of two sequences.
// The set difference is the members of the first sequence
// that don't appear in the second sequence.
func (q Query) ExceptBy(
q2 Query, selector func(interface{}) interface{}) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
next2 := q2.Iterate()
set := make(map[interface{}]bool)
for i, ok := next2(); ok; i, ok = next2() {
s := selector(i)
set[s] = true
}
return func() (item interface{}, ok bool) {
for item, ok = next(); ok; item, ok = next() {
s := selector(item)
if _, has := set[s]; !has {
return
}
}
return
}
},
}
}
+172
View File
@@ -0,0 +1,172 @@
package linq
import "reflect"
// Iterator is an alias for function to iterate over data.
type Iterator func() (item interface{}, ok bool)
// Query is the type returned from query functions.
// It can be iterated manually as shown in the example.
type Query struct {
Iterate func() Iterator
}
// KeyValue is a type that is used to iterate over a map
// (if query is created from a map). This type is also used by
// ToMap() method to output result of a query into a map.
type KeyValue struct {
Key interface{}
Value interface{}
}
// Iterable is an interface that has to be implemented by a
// custom collection in order to work with linq.
type Iterable interface {
Iterate() Iterator
}
// From initializes a linq query with passed slice, array or map
// as the source. String, channel or struct implementing Iterable
// interface can be used as an input. In this case From delegates it
// to FromString, FromChannel and FromIterable internally.
func From(source interface{}) Query {
src := reflect.ValueOf(source)
switch src.Kind() {
case reflect.Slice, reflect.Array:
len := src.Len()
return Query{
Iterate: func() Iterator {
index := 0
return func() (item interface{}, ok bool) {
ok = index < len
if ok {
item = src.Index(index).Interface()
index++
}
return
}
},
}
case reflect.Map:
len := src.Len()
return Query{
Iterate: func() Iterator {
index := 0
keys := src.MapKeys()
return func() (item interface{}, ok bool) {
ok = index < len
if ok {
key := keys[index]
item = KeyValue{
Key: key.Interface(),
Value: src.MapIndex(key).Interface(),
}
index++
}
return
}
},
}
case reflect.String:
return FromString(source.(string))
case reflect.Chan:
return FromChannel(source.(chan interface{}))
default:
return FromIterable(source.(Iterable))
}
}
// FromChannel initializes a linq query with passed channel,
// linq iterates over channel until it is closed.
func FromChannel(source <-chan interface{}) Query {
return Query{
Iterate: func() Iterator {
return func() (item interface{}, ok bool) {
item, ok = <-source
return
}
},
}
}
// FromString initializes a linq query with passed string,
// linq iterates over runes of string.
func FromString(source string) Query {
runes := []rune(source)
len := len(runes)
return Query{
Iterate: func() Iterator {
index := 0
return func() (item interface{}, ok bool) {
ok = index < len
if ok {
item = runes[index]
index++
}
return
}
},
}
}
// FromIterable initializes a linq query with custom collection passed.
// This collection has to implement Iterable interface, linq iterates over items,
// that has to implement Comparable interface or be basic types.
func FromIterable(source Iterable) Query {
return Query{
Iterate: source.Iterate,
}
}
// Range generates a sequence of integral numbers within a specified range.
func Range(start, count int) Query {
return Query{
Iterate: func() Iterator {
index := 0
current := start
return func() (item interface{}, ok bool) {
if index >= count {
return nil, false
}
item, ok = current, true
index++
current++
return
}
},
}
}
// Repeat generates a sequence that contains one repeated value.
func Repeat(value interface{}, count int) Query {
return Query{
Iterate: func() Iterator {
index := 0
return func() (item interface{}, ok bool) {
if index >= count {
return nil, false
}
item, ok = value, true
index++
return
}
},
}
}
+48
View File
@@ -0,0 +1,48 @@
package linq
// Group is a type that is used to store the result of GroupBy method.
type Group struct {
Key interface{}
Group []interface{}
}
// GroupBy method groups the elements of a collection according
// to a specified key selector function and projects the elements for each group
// by using a specified function.
func (q Query) GroupBy(
keySelector func(interface{}) interface{},
elementSelector func(interface{}) interface{},
) Query {
return Query{
func() Iterator {
next := q.Iterate()
set := make(map[interface{}][]interface{})
for item, ok := next(); ok; item, ok = next() {
key := keySelector(item)
set[key] = append(set[key], elementSelector(item))
}
len := len(set)
idx := 0
groups := make([]Group, len)
for k, v := range set {
groups[idx] = Group{k, v}
idx++
}
index := 0
return func() (item interface{}, ok bool) {
ok = index < len
if ok {
item = groups[index]
index++
}
return
}
},
}
}
+50
View File
@@ -0,0 +1,50 @@
package linq
// GroupJoin correlates the elements of two collections based on key equality,
// and groups the results.
//
// This method produces hierarchical results, which means that elements from outer query
// are paired with collections of matching elements from inner. GroupJoin enables you
// to base your results on a whole set of matches for each element of outer query.
//
// The resultSelector function is called only one time for each outer element
// together with a collection of all the inner elements that match the outer element.
// This differs from the Join method, in which the result selector function is invoked
// on pairs that contain one element from outer and one element from inner.
//
// GroupJoin preserves the order of the elements of outer, and for each element of outer,
// the order of the matching elements from inner.
func (q Query) GroupJoin(
inner Query,
outerKeySelector func(interface{}) interface{},
innerKeySelector func(interface{}) interface{},
resultSelector func(outer interface{}, inners []interface{}) interface{},
) Query {
return Query{
Iterate: func() Iterator {
outernext := q.Iterate()
innernext := inner.Iterate()
innerLookup := make(map[interface{}][]interface{})
for innerItem, ok := innernext(); ok; innerItem, ok = innernext() {
innerKey := innerKeySelector(innerItem)
innerLookup[innerKey] = append(innerLookup[innerKey], innerItem)
}
return func() (item interface{}, ok bool) {
if item, ok = outernext(); !ok {
return
}
if group, has := innerLookup[outerKeySelector(item)]; !has {
item = resultSelector(item, []interface{}{})
} else {
item = resultSelector(item, group)
}
return
}
},
}
}
+67
View File
@@ -0,0 +1,67 @@
package linq
// Intersect produces the set intersection of the source collection and the
// provided input collection. The intersection of two sets A and B is defined as
// the set that contains all the elements of A that also appear in B,
// but no other elements.
func (q Query) Intersect(q2 Query) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
next2 := q2.Iterate()
set := make(map[interface{}]bool)
for item, ok := next2(); ok; item, ok = next2() {
set[item] = true
}
return func() (item interface{}, ok bool) {
for item, ok = next(); ok; item, ok = next() {
if _, has := set[item]; has {
delete(set, item)
return
}
}
return
}
},
}
}
// IntersectBy produces the set intersection of the source collection and the
// provided input collection. The intersection of two sets A and B is defined as
// the set that contains all the elements of A that also appear in B,
// but no other elements.
//
// IntersectBy invokes a transform function on each element of both collections.
func (q Query) IntersectBy(
q2 Query,
selector func(interface{}) interface{},
) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
next2 := q2.Iterate()
set := make(map[interface{}]bool)
for item, ok := next2(); ok; item, ok = next2() {
s := selector(item)
set[s] = true
}
return func() (item interface{}, ok bool) {
for item, ok = next(); ok; item, ok = next() {
s := selector(item)
if _, has := set[s]; has {
delete(set, s)
return
}
}
return
}
},
}
}
+56
View File
@@ -0,0 +1,56 @@
package linq
// Join correlates the elements of two collection based on matching keys.
//
// A join refers to the operation of correlating the elements of two sources
// of information based on a common key. Join brings the two information sources
// and the keys by which they are matched together in one method call.
// This differs from the use of SelectMany, which requires more than one method call
// to perform the same operation.
//
// Join preserves the order of the elements of outer collection,
// and for each of these elements, the order of the matching elements of inner.
func (q Query) Join(
inner Query,
outerKeySelector func(interface{}) interface{},
innerKeySelector func(interface{}) interface{},
resultSelector func(outer interface{}, inner interface{}) interface{},
) Query {
return Query{
Iterate: func() Iterator {
outernext := q.Iterate()
innernext := inner.Iterate()
innerLookup := make(map[interface{}][]interface{})
for innerItem, ok := innernext(); ok; innerItem, ok = innernext() {
innerKey := innerKeySelector(innerItem)
innerLookup[innerKey] = append(innerLookup[innerKey], innerItem)
}
var outerItem interface{}
var innerGroup []interface{}
innerLen, innerIndex := 0, 0
return func() (item interface{}, ok bool) {
if innerIndex >= innerLen {
has := false
for !has {
outerItem, ok = outernext()
if !ok {
return
}
innerGroup, has = innerLookup[outerKeySelector(outerItem)]
innerLen = len(innerGroup)
innerIndex = 0
}
}
item = resultSelector(outerItem, innerGroup[innerIndex])
innerIndex++
return item, true
}
},
}
}
+212
View File
@@ -0,0 +1,212 @@
package linq
import "sort"
type order struct {
selector func(interface{}) interface{}
compare comparer
desc bool
}
// OrderedQuery is the type returned from OrderBy, OrderByDescending
// ThenBy and ThenByDescending functions.
type OrderedQuery struct {
Query
original Query
orders []order
}
// OrderBy sorts the elements of a collection in ascending order.
// Elements are sorted according to a key.
func (q Query) OrderBy(
selector func(interface{}) interface{}) OrderedQuery {
return OrderedQuery{
orders: []order{{selector: selector}},
original: q,
Query: Query{
Iterate: func() Iterator {
items := q.sort([]order{{selector: selector}})
len := len(items)
index := 0
return func() (item interface{}, ok bool) {
ok = index < len
if ok {
item = items[index]
index++
}
return
}
},
},
}
}
// OrderByDescending sorts the elements of a collection in descending order.
// Elements are sorted according to a key.
func (q Query) OrderByDescending(
selector func(interface{}) interface{}) OrderedQuery {
return OrderedQuery{
orders: []order{{selector: selector, desc: true}},
original: q,
Query: Query{
Iterate: func() Iterator {
items := q.sort([]order{{selector: selector, desc: true}})
len := len(items)
index := 0
return func() (item interface{}, ok bool) {
ok = index < len
if ok {
item = items[index]
index++
}
return
}
},
},
}
}
// ThenBy performs a subsequent ordering of the elements in a collection
// in ascending order. This method enables you to specify multiple sort criteria
// by applying any number of ThenBy or ThenByDescending methods.
func (oq OrderedQuery) ThenBy(
selector func(interface{}) interface{}) OrderedQuery {
return OrderedQuery{
orders: append(oq.orders, order{selector: selector}),
original: oq.original,
Query: Query{
Iterate: func() Iterator {
items := oq.original.sort(append(oq.orders, order{selector: selector}))
len := len(items)
index := 0
return func() (item interface{}, ok bool) {
ok = index < len
if ok {
item = items[index]
index++
}
return
}
},
},
}
}
// ThenByDescending performs a subsequent ordering of the elements in a collection
// in descending order. This method enables you to specify multiple sort criteria
// by applying any number of ThenBy or ThenByDescending methods.
func (oq OrderedQuery) ThenByDescending(
selector func(interface{}) interface{}) OrderedQuery {
return OrderedQuery{
orders: append(oq.orders, order{selector: selector, desc: true}),
original: oq.original,
Query: Query{
Iterate: func() Iterator {
items := oq.original.sort(append(oq.orders, order{selector: selector, desc: true}))
len := len(items)
index := 0
return func() (item interface{}, ok bool) {
ok = index < len
if ok {
item = items[index]
index++
}
return
}
},
},
}
}
// Sort returns a new query by sorting elements with provided less function
// in ascending order. The comparer function should return true if the parameter i
// is less than j. While this method is uglier than chaining OrderBy, OrderByDescending,
// ThenBy and ThenByDescending methods, it's performance is much better.
func (q Query) Sort(less func(i, j interface{}) bool) Query {
return Query{
Iterate: func() Iterator {
items := q.lessSort(less)
len := len(items)
index := 0
return func() (item interface{}, ok bool) {
ok = index < len
if ok {
item = items[index]
index++
}
return
}
},
}
}
type sorter struct {
items []interface{}
less func(i, j interface{}) bool
}
func (s sorter) Len() int {
return len(s.items)
}
func (s sorter) Swap(i, j int) {
s.items[i], s.items[j] = s.items[j], s.items[i]
}
func (s sorter) Less(i, j int) bool {
return s.less(s.items[i], s.items[j])
}
func (q Query) sort(orders []order) (r []interface{}) {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
r = append(r, item)
}
for i, j := range orders {
orders[i].compare = getComparer(j.selector(r[0]))
}
s := sorter{
items: r,
less: func(i, j interface{}) bool {
for _, order := range orders {
x, y := order.selector(i), order.selector(j)
switch order.compare(x, y) {
case 0:
continue
case -1:
return !order.desc
default:
return order.desc
}
}
return false
}}
sort.Sort(s)
return
}
func (q Query) lessSort(less func(i, j interface{}) bool) (r []interface{}) {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
r = append(r, item)
}
s := sorter{items: r, less: less}
sort.Sort(s)
return
}
+394
View File
@@ -0,0 +1,394 @@
package linq
import (
"math"
"reflect"
)
// All determines whether all elements of a collection satisfy a condition.
func (q Query) All(predicate func(interface{}) bool) bool {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
if !predicate(item) {
return false
}
}
return true
}
// Any determines whether any element of a collection exists.
func (q Query) Any() bool {
_, ok := q.Iterate()()
return ok
}
// AnyWith determines whether any element of a collection satisfies a condition.
func (q Query) AnyWith(predicate func(interface{}) bool) bool {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
if predicate(item) {
return true
}
}
return false
}
// Average computes the average of a collection of numeric values.
func (q Query) Average() (r float64) {
next := q.Iterate()
item, ok := next()
if !ok {
return math.NaN()
}
n := 1
switch item.(type) {
case int, int8, int16, int32, int64:
conv := getIntConverter(item)
sum := conv(item)
for item, ok = next(); ok; item, ok = next() {
sum += conv(item)
n++
}
r = float64(sum)
case uint, uint8, uint16, uint32, uint64:
conv := getUIntConverter(item)
sum := conv(item)
for item, ok = next(); ok; item, ok = next() {
sum += conv(item)
n++
}
r = float64(sum)
default:
conv := getFloatConverter(item)
r = conv(item)
for item, ok = next(); ok; item, ok = next() {
r += conv(item)
n++
}
}
return r / float64(n)
}
// Contains determines whether a collection contains a specified element.
func (q Query) Contains(value interface{}) bool {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
if item == value {
return true
}
}
return false
}
// Count returns the number of elements in a collection.
func (q Query) Count() (r int) {
next := q.Iterate()
for _, ok := next(); ok; _, ok = next() {
r++
}
return
}
// CountWith returns a number that represents how many elements
// in the specified collection satisfy a condition.
func (q Query) CountWith(predicate func(interface{}) bool) (r int) {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
if predicate(item) {
r++
}
}
return
}
// First returns the first element of a collection.
func (q Query) First() interface{} {
item, _ := q.Iterate()()
return item
}
// FirstWith returns the first element of a collection that satisfies
// a specified condition.
func (q Query) FirstWith(predicate func(interface{}) bool) interface{} {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
if predicate(item) {
return item
}
}
return nil
}
// Last returns the last element of a collection.
func (q Query) Last() (r interface{}) {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
r = item
}
return
}
// LastWith returns the last element of a collection that satisfies
// a specified condition.
func (q Query) LastWith(predicate func(interface{}) bool) (r interface{}) {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
if predicate(item) {
r = item
}
}
return
}
// Max returns the maximum value in a collection of values.
func (q Query) Max() (r interface{}) {
next := q.Iterate()
item, ok := next()
if !ok {
return nil
}
compare := getComparer(item)
r = item
for item, ok := next(); ok; item, ok = next() {
if compare(item, r) > 0 {
r = item
}
}
return
}
// Min returns the minimum value in a collection of values.
func (q Query) Min() (r interface{}) {
next := q.Iterate()
item, ok := next()
if !ok {
return nil
}
compare := getComparer(item)
r = item
for item, ok := next(); ok; item, ok = next() {
if compare(item, r) < 0 {
r = item
}
}
return
}
// Results iterates over a collection and returnes slice of interfaces
func (q Query) Results() (r []interface{}) {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
r = append(r, item)
}
return
}
// SequenceEqual determines whether two collections are equal.
func (q Query) SequenceEqual(q2 Query) bool {
next := q.Iterate()
next2 := q2.Iterate()
for item, ok := next(); ok; item, ok = next() {
item2, ok2 := next2()
if !ok2 || item != item2 {
return false
}
}
_, ok2 := next2()
return !ok2
}
// Single returns the only element of a collection, and nil
// if there is not exactly one element in the collection.
func (q Query) Single() interface{} {
next := q.Iterate()
item, ok := next()
if !ok {
return nil
}
_, ok = next()
if ok {
return nil
}
return item
}
// SingleWith returns the only element of a collection that satisfies
// a specified condition, and nil if more than one such element exists.
func (q Query) SingleWith(predicate func(interface{}) bool) (r interface{}) {
next := q.Iterate()
found := false
for item, ok := next(); ok; item, ok = next() {
if predicate(item) {
if found {
return nil
}
found = true
r = item
}
}
return
}
// SumInts computes the sum of a collection of numeric values.
//
// Values can be of any integer type: int, int8, int16, int32, int64.
// The result is int64. Method returns zero if collection contains no elements.
func (q Query) SumInts() (r int64) {
next := q.Iterate()
item, ok := next()
if !ok {
return 0
}
conv := getIntConverter(item)
r = conv(item)
for item, ok = next(); ok; item, ok = next() {
r += conv(item)
}
return
}
// SumUInts computes the sum of a collection of numeric values.
//
// Values can be of any unsigned integer type: uint, uint8, uint16, uint32, uint64.
// The result is uint64. Method returns zero if collection contains no elements.
func (q Query) SumUInts() (r uint64) {
next := q.Iterate()
item, ok := next()
if !ok {
return 0
}
conv := getUIntConverter(item)
r = conv(item)
for item, ok = next(); ok; item, ok = next() {
r += conv(item)
}
return
}
// SumFloats computes the sum of a collection of numeric values.
//
// Values can be of any float type: float32 or float64. The result is float64.
// Method returns zero if collection contains no elements.
func (q Query) SumFloats() (r float64) {
next := q.Iterate()
item, ok := next()
if !ok {
return 0
}
conv := getFloatConverter(item)
r = conv(item)
for item, ok = next(); ok; item, ok = next() {
r += conv(item)
}
return
}
// ToChannel iterates over a collection and outputs each element
// to a channel, then closes it.
func (q Query) ToChannel(result chan<- interface{}) {
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
result <- item
}
close(result)
}
// ToMap iterates over a collection and populates result map with elements.
// Collection elements have to be of KeyValue type to use this method.
// To populate a map with elements of different type use ToMapBy method.
func (q Query) ToMap(result interface{}) {
q.ToMapBy(
result,
func(i interface{}) interface{} {
return i.(KeyValue).Key
},
func(i interface{}) interface{} {
return i.(KeyValue).Value
})
}
// ToMapBy iterates over a collection and populates result map with elements.
// Functions keySelector and valueSelector are executed for each element of the collection
// to generate key and value for the map. Generated key and value types must be assignable
// to the map's key and value types.
func (q Query) ToMapBy(
result interface{},
keySelector func(interface{}) interface{},
valueSelector func(interface{}) interface{},
) {
res := reflect.ValueOf(result)
m := reflect.Indirect(res)
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
key := reflect.ValueOf(keySelector(item))
value := reflect.ValueOf(valueSelector(item))
m.SetMapIndex(key, value)
}
res.Elem().Set(m)
}
// ToSlice iterates over a collection and populates result slice with elements.
// Collection elements must be assignable to the slice's element type.
func (q Query) ToSlice(result interface{}) {
res := reflect.ValueOf(result)
slice := reflect.Indirect(res)
next := q.Iterate()
for item, ok := next(); ok; item, ok = next() {
slice = reflect.Append(slice, reflect.ValueOf(item))
}
res.Elem().Set(slice)
}
+30
View File
@@ -0,0 +1,30 @@
package linq
// Reverse inverts the order of the elements in a collection.
//
// Unlike OrderBy, this sorting method does not consider the actual values themselves
// in determining the order. Rather, it just returns the elements in the reverse order
// from which they are produced by the underlying source.
func (q Query) Reverse() Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
items := []interface{}{}
for item, ok := next(); ok; item, ok = next() {
items = append(items, item)
}
index := len(items) - 1
return func() (item interface{}, ok bool) {
if index < 0 {
return
}
item, ok = items[index], true
index--
return
}
},
}
}
+71
View File
@@ -0,0 +1,71 @@
package linq
// Select projects each element of a collection into a new form.
// Returns a query with the result of invoking the transform function
// on each element of original source.
//
// This projection method requires the transform function, selector,
// to produce one value for each value in the source collection.
// If selector returns a value that is itself a collection,
// it is up to the consumer to traverse the subcollections manually.
// In such a situation, it might be better for your query to return a single
// coalesced collection of values. To achieve this, use the SelectMany method
// instead of Select. Although SelectMany works similarly to Select,
// it differs in that the transform function returns a collection
// that is then expanded by SelectMany before it is returned.
func (q Query) Select(selector func(interface{}) interface{}) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
return func() (item interface{}, ok bool) {
var it interface{}
it, ok = next()
if ok {
item = selector(it)
}
return
}
},
}
}
// SelectIndexed projects each element of a collection into a new form
// by incorporating the element's index. Returns a query with the result
// of invoking the transform function on each element of original source.
//
// The first argument to selector represents the zero-based index of that element
// in the source collection. This can be useful if the elements are in a known order
// and you want to do something with an element at a particular index,
// for example. It can also be useful if you want to retrieve the index of one
// or more elements. The second argument to selector represents the element to process.
//
// This projection method requires the transform function, selector,
// to produce one value for each value in the source collection.
// If selector returns a value that is itself a collection,
// it is up to the consumer to traverse the subcollections manually.
// In such a situation, it might be better for your query to return a single
// coalesced collection of values. To achieve this, use the SelectMany method
// instead of Select. Although SelectMany works similarly to Select,
// it differs in that the transform function returns a collection
// that is then expanded by SelectMany before it is returned.
func (q Query) SelectIndexed(selector func(int, interface{}) interface{}) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
index := 0
return func() (item interface{}, ok bool) {
var it interface{}
it, ok = next()
if ok {
item = selector(index, it)
index++
}
return
}
},
}
}
+151
View File
@@ -0,0 +1,151 @@
package linq
// SelectMany projects each element of a collection to a Query, iterates and
// flattens the resulting collection into one collection.
func (q Query) SelectMany(selector func(interface{}) Query) Query {
return Query{
Iterate: func() Iterator {
outernext := q.Iterate()
var inner interface{}
var innernext Iterator
return func() (item interface{}, ok bool) {
for !ok {
if inner == nil {
inner, ok = outernext()
if !ok {
return
}
innernext = selector(inner).Iterate()
}
item, ok = innernext()
if !ok {
inner = nil
}
}
return
}
},
}
}
// SelectManyIndexed projects each element of a collection to a Query, iterates and
// flattens the resulting collection into one collection.
//
// The first argument to selector represents the zero-based index of that element
// in the source collection. This can be useful if the elements are in a known order
// and you want to do something with an element at a particular index, for example.
// It can also be useful if you want to retrieve the index of one or more elements.
// The second argument to selector represents the element to process.
func (q Query) SelectManyIndexed(selector func(int, interface{}) Query) Query {
return Query{
Iterate: func() Iterator {
outernext := q.Iterate()
index := 0
var inner interface{}
var innernext Iterator
return func() (item interface{}, ok bool) {
for !ok {
if inner == nil {
inner, ok = outernext()
if !ok {
return
}
innernext = selector(index, inner).Iterate()
index++
}
item, ok = innernext()
if !ok {
inner = nil
}
}
return
}
},
}
}
// SelectManyBy projects each element of a collection to a Query, iterates and
// flattens the resulting collection into one collection, and invokes
// a result selector function on each element therein.
func (q Query) SelectManyBy(
selector func(interface{}) Query,
resultSelector func(interface{}, interface{}) interface{},
) Query {
return Query{
Iterate: func() Iterator {
outernext := q.Iterate()
var outer interface{}
var innernext Iterator
return func() (item interface{}, ok bool) {
for !ok {
if outer == nil {
outer, ok = outernext()
if !ok {
return
}
innernext = selector(outer).Iterate()
}
item, ok = innernext()
if !ok {
outer = nil
}
}
item = resultSelector(outer, item)
return
}
},
}
}
// SelectManyByIndexed projects each element of a collection to a Query, iterates and
// flattens the resulting collection into one collection, and invokes
// a result selector function on each element therein.
// The index of each source element is used in the intermediate projected form
// of that element.
func (q Query) SelectManyByIndexed(selector func(int, interface{}) Query,
resultSelector func(interface{}, interface{}) interface{}) Query {
return Query{
Iterate: func() Iterator {
outernext := q.Iterate()
index := 0
var outer interface{}
var innernext Iterator
return func() (item interface{}, ok bool) {
for !ok {
if outer == nil {
outer, ok = outernext()
if !ok {
return
}
innernext = selector(index, outer).Iterate()
index++
}
item, ok = innernext()
if !ok {
outer = nil
}
}
item = resultSelector(outer, item)
return
}
},
}
}
+91
View File
@@ -0,0 +1,91 @@
package linq
// Skip bypasses a specified number of elements in a collection
// and then returns the remaining elements.
func (q Query) Skip(count int) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
n := count
return func() (item interface{}, ok bool) {
for ; n > 0; n-- {
item, ok = next()
if !ok {
return
}
}
return next()
}
},
}
}
// SkipWhile bypasses elements in a collection as long as a specified condition is true
// and then returns the remaining elements.
//
// This method tests each element by using predicate and skips the element
// if the result is true. After the predicate function returns false for an element,
// that element and the remaining elements in source are returned
// and there are no more invocations of predicate.
func (q Query) SkipWhile(predicate func(interface{}) bool) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
ready := false
return func() (item interface{}, ok bool) {
for !ready {
item, ok = next()
if !ok {
return
}
ready = !predicate(item)
if ready {
return
}
}
return next()
}
},
}
}
// SkipWhileIndexed bypasses elements in a collection as long as a specified condition
// is true and then returns the remaining elements. The element's index is used
// in the logic of the predicate function.
//
// This method tests each element by using predicate and skips the element
// if the result is true. After the predicate function returns false for an element,
// that element and the remaining elements in source are returned
// and there are no more invocations of predicate.
func (q Query) SkipWhileIndexed(predicate func(int, interface{}) bool) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
ready := false
index := 0
return func() (item interface{}, ok bool) {
for !ready {
item, ok = next()
if !ok {
return
}
ready = !predicate(index, item)
if ready {
return
}
index++
}
return next()
}
},
}
}
+84
View File
@@ -0,0 +1,84 @@
package linq
// Take returns a specified number of contiguous elements from the start of a collection.
func (q Query) Take(count int) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
n := count
return func() (item interface{}, ok bool) {
if n <= 0 {
return
}
n--
return next()
}
},
}
}
// TakeWhile returns elements from a collection as long as a specified condition is true,
// and then skips the remaining elements.
func (q Query) TakeWhile(predicate func(interface{}) bool) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
done := false
return func() (item interface{}, ok bool) {
if done {
return
}
item, ok = next()
if !ok {
done = true
return
}
if predicate(item) {
return
}
done = true
return nil, false
}
},
}
}
// TakeWhileIndexed returns elements from a collection as long as a specified condition
// is true. The element's index is used in the logic of the predicate function.
// The first argument of predicate represents the zero-based index of the element
// within collection. The second argument represents the element to test.
func (q Query) TakeWhileIndexed(predicate func(int, interface{}) bool) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
done := false
index := 0
return func() (item interface{}, ok bool) {
if done {
return
}
item, ok = next()
if !ok {
done = true
return
}
if predicate(index, item) {
index++
return
}
done = true
return nil, false
}
},
}
}
+41
View File
@@ -0,0 +1,41 @@
package linq
// Union produces the set union of two collections.
//
// This method excludes duplicates from the return set.
// This is different behavior to the Concat method,
// which returns all the elements in the input collection
// including duplicates.
func (q Query) Union(q2 Query) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
next2 := q2.Iterate()
set := make(map[interface{}]bool)
use1 := true
return func() (item interface{}, ok bool) {
if use1 {
for item, ok = next(); ok; item, ok = next() {
if _, has := set[item]; !has {
set[item] = true
return
}
}
use1 = false
}
for item, ok = next2(); ok; item, ok = next2() {
if _, has := set[item]; !has {
set[item] = true
return
}
}
return
}
},
}
}
+46
View File
@@ -0,0 +1,46 @@
package linq
// Where filters a collection of values based on a predicate.
func (q Query) Where(predicate func(interface{}) bool) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
return func() (item interface{}, ok bool) {
for item, ok = next(); ok; item, ok = next() {
if predicate(item) {
return
}
}
return
}
},
}
}
// WhereIndexed filters a collection of values based on a predicate.
// Each element's index is used in the logic of the predicate function.
//
// The first argument represents the zero-based index of the element within collection.
// The second argument of predicate represents the element to test.
func (q Query) WhereIndexed(predicate func(int, interface{}) bool) Query {
return Query{
Iterate: func() Iterator {
next := q.Iterate()
index := 0
return func() (item interface{}, ok bool) {
for item, ok = next(); ok; item, ok = next() {
if predicate(index, item) {
return
}
index++
}
return
}
},
}
}
+35
View File
@@ -0,0 +1,35 @@
package linq
// Zip applies a specified function to the corresponding elements
// of two collections, producing a collection of the results.
//
// The method steps through the two input collections, applying function
// resultSelector to corresponding elements of the two collections.
// The method returns a collection of the values that are returned by resultSelector.
// If the input collections do not have the same number of elements,
// the method combines elements until it reaches the end of one of the collections.
// For example, if one collection has three elements and the other one has four,
// the result collection has only three elements.
func (q Query) Zip(
q2 Query,
resultSelector func(interface{}, interface{}) interface{},
) Query {
return Query{
Iterate: func() Iterator {
next1 := q.Iterate()
next2 := q2.Iterate()
return func() (item interface{}, ok bool) {
item1, ok1 := next1()
item2, ok2 := next2()
if ok1 && ok2 {
return resultSelector(item1, item2), true
}
return nil, false
}
},
}
}
+2
View File
@@ -1,2 +1,4 @@
# github.com/ahmetalpbalkan/go-linq v2.0.0-rc0+incompatible
github.com/ahmetalpbalkan/go-linq
# github.com/go-ini/ini v1.57.0
github.com/go-ini/ini