feat(Go-Tool):2020/11/23:修改项目案例(按照一定规则对一组数据进行排序分组)
This commit is contained in:
-14
@@ -1,14 +0,0 @@
|
||||
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.
|
||||
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
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
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
// 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
@@ -1,59 +0,0 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
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
@@ -1,50 +0,0 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
-56
@@ -1,56 +0,0 @@
|
||||
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
@@ -1,212 +0,0 @@
|
||||
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
@@ -1,394 +0,0 @@
|
||||
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)
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
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
@@ -1,151 +0,0 @@
|
||||
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
@@ -1,91 +0,0 @@
|
||||
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
@@ -1,84 +0,0 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
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
@@ -1,35 +0,0 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Generated
Generated
Vendored
+1
-2
@@ -2,14 +2,13 @@ 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 ./...
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2016 Ahmet Alp Balkan
|
||||
|
||||
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.
|
||||
Generated
Vendored
+91
-10
@@ -1,16 +1,21 @@
|
||||
# go-linq [](https://godoc.org/github.com/ahmetalpbalkan/go-linq) [](https://travis-ci.org/ahmetalpbalkan/go-linq) [](https://coveralls.io/github/ahmetalpbalkan/go-linq?branch=master) [](https://goreportcard.com/report/github.com/ahmetalpbalkan/go-linq)
|
||||
A powerful language integrated query (LINQ) library for Go.
|
||||
* Written in vanilla Go!
|
||||
* Safe for concurrent use
|
||||
* Written in vanilla Go, no dependencies!
|
||||
* Complete lazy evaluation with iterator pattern
|
||||
* Safe for concurrent use
|
||||
* Supports generic functions to make your code cleaner and free of type assertions
|
||||
* Supports arrays, slices, maps, strings, channels and custom collections
|
||||
(collection needs to implement `Iterable` interface and element - `Comparable`
|
||||
interface)
|
||||
|
||||
## Installation
|
||||
|
||||
$ go get github.com/ahmetalpbalkan/go-linq
|
||||
|
||||
We recommend using a dependency manager (e.g. [govendor][govendor] or
|
||||
[godep][godep]) to maintain a local copy of this package in your project.
|
||||
|
||||
[govendor]: https://github.com/kardianos/govendor
|
||||
[godep]: https://github.com/tools/godep/
|
||||
|
||||
> :warning: :warning: `go-linq` has recently introduced _breaking API changes_
|
||||
> with v2.0.0. See [release notes](#release-notes) for details. v2.0.0 comes with
|
||||
> a refined interface, dramatically increased performance and memory efficiency,
|
||||
@@ -26,16 +31,20 @@ Usage is as easy as chaining methods like:
|
||||
|
||||
`From(slice)` `.Where(predicate)` `.Select(selector)` `.Union(data)`
|
||||
|
||||
**Example: Find all owners of cars manufactured from 2015**
|
||||
**Example 1: Find all owners of cars manufactured after 2015**
|
||||
|
||||
```go
|
||||
import . "github.com/ahmetalpbalkan/go-linq"
|
||||
|
||||
type Car struct {
|
||||
id, year int
|
||||
year int
|
||||
owner, model string
|
||||
}
|
||||
|
||||
owners := []string{}
|
||||
...
|
||||
|
||||
|
||||
var owners []string
|
||||
|
||||
From(cars).Where(func(c interface{}) bool {
|
||||
return c.(Car).year >= 2015
|
||||
@@ -44,7 +53,21 @@ From(cars).Where(func(c interface{}) bool {
|
||||
}).ToSlice(&owners)
|
||||
```
|
||||
|
||||
**Example: Find the author who has written the most books**
|
||||
Or, you can use generic functions, like `WhereT` and `SelectT` to simplify your code
|
||||
(at a performance penalty):
|
||||
|
||||
```go
|
||||
var owners []string
|
||||
|
||||
From(cars).WhereT(func(c Car) bool {
|
||||
return c.year >= 2015
|
||||
}).SelectT(func(c Car) string {
|
||||
return c.owner
|
||||
}).ToSlice(&owners)
|
||||
```
|
||||
|
||||
**Example 2: Find the author who has written the most books**
|
||||
|
||||
```go
|
||||
import . "github.com/ahmetalpbalkan/go-linq"
|
||||
|
||||
@@ -71,7 +94,8 @@ author := From(books).SelectMany( // make a flat array of authors
|
||||
}).First() // take the first author
|
||||
```
|
||||
|
||||
**Example: Implement a custom method that leaves only values greater than the specified threshold**
|
||||
**Example 3: Implement a custom method that leaves only values greater than the specified threshold**
|
||||
|
||||
```go
|
||||
type MyQuery Query
|
||||
|
||||
@@ -96,10 +120,67 @@ func (q MyQuery) GreaterThan(threshold int) Query {
|
||||
result := MyQuery(Range(1,10)).GreaterThan(5).Results()
|
||||
```
|
||||
|
||||
**More examples** can be found in [documentation](https://godoc.org/github.com/ahmetalpbalkan/go-linq).
|
||||
## Generic Functions
|
||||
|
||||
Although Go doesn't implement generics, with some reflection tricks, you can use go-linq without
|
||||
typing `interface{}`s and type assertions. This will introduce a performance penalty (5x-10x slower)
|
||||
but will yield in a cleaner and more readable code.
|
||||
|
||||
Methods with `T` suffix (such as `WhereT`) accept functions with generic types. So instead of
|
||||
|
||||
.Select(func(v interface{}) interface{} {...})
|
||||
|
||||
you can type:
|
||||
|
||||
.SelectT(func(v YourType) YourOtherType {...})
|
||||
|
||||
This will make your code free of `interface{}` and type assertions.
|
||||
|
||||
**Example 4: "MapReduce" in a slice of string sentences to list the top 5 most used words using generic functions**
|
||||
|
||||
```go
|
||||
var results []string
|
||||
|
||||
From(sentences).
|
||||
// split sentences to words
|
||||
SelectManyT(func(sentence string) Query {
|
||||
return From(strings.Split(sentence, " "))
|
||||
}).
|
||||
// group the words
|
||||
GroupByT(
|
||||
func(word string) string { return word },
|
||||
func(word string) string { return word },
|
||||
).
|
||||
// order by count
|
||||
OrderByDescendingT(func(wordGroup Group) int {
|
||||
return len(wordGroup.Group)
|
||||
}).
|
||||
// order by the word
|
||||
ThenByT(func(wordGroup Group) string {
|
||||
return wordGroup.Key.(string)
|
||||
}).
|
||||
Take(5). // take the top 5
|
||||
// project the words using the index as rank
|
||||
SelectIndexedT(func(index int, wordGroup Group) string {
|
||||
return fmt.Sprintf("Rank: #%d, Word: %s, Counts: %d", index+1, wordGroup.Key, len(wordGroup.Group))
|
||||
}).
|
||||
ToSlice(&results)
|
||||
```
|
||||
|
||||
**More examples** can be found in the [documentation](https://godoc.org/github.com/ahmetalpbalkan/go-linq).
|
||||
|
||||
## Release Notes
|
||||
|
||||
~~~
|
||||
v3.0.0 (2017-01-10)
|
||||
* Breaking change: ToSlice() now overwrites existing slice starting
|
||||
from index 0 and grows/reslices it as needed.
|
||||
* Generic methods support (thanks @cleitonmarx!)
|
||||
- Accepting parametrized functions was originally proposed in #26
|
||||
- You can now avoid type assertions and interface{}s
|
||||
- Functions with generic methods are named as "MethodNameT" and
|
||||
signature for the existing LINQ methods are unchanged.
|
||||
* Added ForEach(), ForEachIndexed() and AggregateWithSeedBy().
|
||||
|
||||
v2.0.0 (2016-09-02)
|
||||
* IMPORTANT: This release is a BREAKING CHANGE. The old version
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
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
|
||||
}
|
||||
|
||||
// AggregateT is the typed version of Aggregate.
|
||||
//
|
||||
// - f is of type: func(TSource, TSource) TSource
|
||||
//
|
||||
// NOTE: Aggregate has better performance than AggregateT.
|
||||
func (q Query) AggregateT(f interface{}) interface{} {
|
||||
fGenericFunc, err := newGenericFunc(
|
||||
"AggregateT", "f", f,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fFunc := func(result interface{}, current interface{}) interface{} {
|
||||
return fGenericFunc.Call(result, current)
|
||||
}
|
||||
|
||||
return q.Aggregate(fFunc)
|
||||
}
|
||||
|
||||
// AggregateWithSeed applies an accumulator function over a sequence. The
|
||||
// specified seed value is used as the initial accumulator value.
|
||||
//
|
||||
// Aggregate method makes it simple to perform a calculation over a sequence of
|
||||
// values. This method works by calling f() one time for each element in source
|
||||
// except the first one. Each time f() is called, Aggregate passes both the
|
||||
// element from the sequence and an aggregated value (as the first argument to
|
||||
// f()). The value of the seed parameter is used as the initial aggregate value.
|
||||
// The result of f() replaces the previous aggregated value.
|
||||
//
|
||||
// Aggregate returns the final result of f().
|
||||
func (q Query) AggregateWithSeed(seed interface{},
|
||||
f func(interface{}, interface{}) interface{}) interface{} {
|
||||
|
||||
next := q.Iterate()
|
||||
result := seed
|
||||
|
||||
for current, ok := next(); ok; current, ok = next() {
|
||||
result = f(result, current)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// AggregateWithSeedT is the typed version of AggregateWithSeed.
|
||||
//
|
||||
// - f is of type "func(TAccumulate, TSource) TAccumulate"
|
||||
//
|
||||
// NOTE: AggregateWithSeed has better performance than
|
||||
// AggregateWithSeedT.
|
||||
func (q Query) AggregateWithSeedT(seed interface{},
|
||||
f interface{}) interface{} {
|
||||
fGenericFunc, err := newGenericFunc(
|
||||
"AggregateWithSeed", "f", f,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fFunc := func(result interface{}, current interface{}) interface{} {
|
||||
return fGenericFunc.Call(result, current)
|
||||
}
|
||||
|
||||
return q.AggregateWithSeed(seed, fFunc)
|
||||
}
|
||||
|
||||
// AggregateWithSeedBy applies an accumulator function over a sequence. The
|
||||
// specified seed value is used as the initial accumulator value, and the
|
||||
// specified function is used to select the result value.
|
||||
//
|
||||
// Aggregate method makes it simple to perform a calculation over a sequence of
|
||||
// values. This method works by calling f() one time for each element in source.
|
||||
// Each time func is called, Aggregate passes both the element from the sequence
|
||||
// and an aggregated value (as the first argument to func). The value of the
|
||||
// seed parameter is used as the initial aggregate value. The result of func
|
||||
// replaces the previous aggregated value.
|
||||
//
|
||||
// The final result of func is passed to resultSelector to obtain the final
|
||||
// result of Aggregate.
|
||||
func (q Query) AggregateWithSeedBy(seed interface{},
|
||||
f func(interface{}, interface{}) interface{},
|
||||
resultSelector func(interface{}) interface{}) interface{} {
|
||||
|
||||
next := q.Iterate()
|
||||
result := seed
|
||||
|
||||
for current, ok := next(); ok; current, ok = next() {
|
||||
result = f(result, current)
|
||||
}
|
||||
|
||||
return resultSelector(result)
|
||||
}
|
||||
|
||||
// AggregateWithSeedByT is the typed version of AggregateWithSeedBy.
|
||||
//
|
||||
// - f is of type "func(TAccumulate, TSource) TAccumulate"
|
||||
// - resultSelectorFn is of type "func(TAccumulate) TResult"
|
||||
//
|
||||
// NOTE: AggregateWithSeedBy has better performance than
|
||||
// AggregateWithSeedByT.
|
||||
func (q Query) AggregateWithSeedByT(seed interface{},
|
||||
f interface{},
|
||||
resultSelectorFn interface{}) interface{} {
|
||||
fGenericFunc, err := newGenericFunc(
|
||||
"AggregateWithSeedByT", "f", f,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fFunc := func(result interface{}, current interface{}) interface{} {
|
||||
return fGenericFunc.Call(result, current)
|
||||
}
|
||||
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"AggregateWithSeedByT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(result interface{}) interface{} {
|
||||
return resultSelectorGenericFunc.Call(result)
|
||||
}
|
||||
|
||||
return q.AggregateWithSeedBy(seed, fFunc, resultSelectorFunc)
|
||||
}
|
||||
Generated
Vendored
+11
-11
@@ -2,21 +2,21 @@ package linq
|
||||
|
||||
type comparer func(interface{}, interface{}) int
|
||||
|
||||
// Comparable is an interface that has to be implemented by a
|
||||
// custom collection elememts in order to work with linq.
|
||||
// Comparable is an interface that has to be implemented by a custom collection
|
||||
// elememts in order to work with linq.
|
||||
//
|
||||
// Example:
|
||||
// func (f foo) CompareTo(c Comparable) int {
|
||||
// a, b := f.f1, c.(foo).f1
|
||||
// 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
|
||||
// }
|
||||
// if a < b {
|
||||
// return -1
|
||||
// } else if a > b {
|
||||
// return 1
|
||||
// }
|
||||
//
|
||||
// return 0
|
||||
// }
|
||||
// return 0
|
||||
// }
|
||||
type Comparable interface {
|
||||
CompareTo(Comparable) int
|
||||
}
|
||||
Generated
Vendored
+6
-6
@@ -1,7 +1,7 @@
|
||||
package linq
|
||||
|
||||
// Append inserts an item to the end of a collection,
|
||||
// so it becomes the last item.
|
||||
// Append inserts an item to the end of a collection, so it becomes the last
|
||||
// item.
|
||||
func (q Query) Append(item interface{}) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -28,8 +28,8 @@ func (q Query) Append(item interface{}) Query {
|
||||
// Concat concatenates two collections.
|
||||
//
|
||||
// The Concat method differs from the Union method because the Concat method
|
||||
// returns all the original elements in the input sequences.
|
||||
// The Union method returns only unique elements.
|
||||
// returns all the original elements in the input sequences. The Union method
|
||||
// returns only unique elements.
|
||||
func (q Query) Concat(q2 Query) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -53,8 +53,8 @@ func (q Query) Concat(q2 Query) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// Prepend inserts an item to the beginning of a collection,
|
||||
// so it becomes the first item.
|
||||
// Prepend inserts an item to the beginning of a collection, so it becomes the
|
||||
// first item.
|
||||
func (q Query) Prepend(item interface{}) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
Generated
Vendored
Generated
Vendored
+28
-5
@@ -1,7 +1,7 @@
|
||||
package linq
|
||||
|
||||
// Distinct method returns distinct elements from a collection.
|
||||
// The result is an unordered collection that contains no duplicate values.
|
||||
// Distinct method returns distinct elements from a collection. The result is an
|
||||
// unordered collection that contains no duplicate values.
|
||||
func (q Query) Distinct() Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -22,11 +22,11 @@ func (q Query) Distinct() Query {
|
||||
}
|
||||
}
|
||||
|
||||
// Distinct method returns distinct elements from a collection.
|
||||
// The result is an ordered collection that contains no duplicate values.
|
||||
// Distinct method returns distinct elements from a collection. The result is an
|
||||
// ordered collection that contains no duplicate values.
|
||||
//
|
||||
// NOTE: Distinct method on OrderedQuery type has better performance than
|
||||
// Distinct method on Query type
|
||||
// Distinct method on Query type.
|
||||
func (oq OrderedQuery) Distinct() OrderedQuery {
|
||||
return OrderedQuery{
|
||||
orders: oq.orders,
|
||||
@@ -73,3 +73,26 @@ func (q Query) DistinctBy(selector func(interface{}) interface{}) Query {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// DistinctByT is the typed version of DistinctBy.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource) TSource".
|
||||
//
|
||||
// NOTE: DistinctBy has better performance than DistinctByT.
|
||||
func (q Query) DistinctByT(selectorFn interface{}) Query {
|
||||
selectorFunc, ok := selectorFn.(func(interface{}) interface{})
|
||||
if !ok {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"DistinctByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc = func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
}
|
||||
return q.DistinctBy(selectorFunc)
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// Package linq provides methods for querying and manipulating slices, arrays,
|
||||
// maps, strings, channels and collections.
|
||||
//
|
||||
// Authors: Alexander Kalankhodzhaev (kalan), Ahmet Alp Balkan, Cleiton Marques
|
||||
// Souza.
|
||||
package linq
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ExceptByT is the typed version of ExceptBy.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource) TSource"
|
||||
//
|
||||
// NOTE: ExceptBy has better performance than ExceptByT.
|
||||
func (q Query) ExceptByT(q2 Query,
|
||||
selectorFn interface{}) Query {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"ExceptByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return q.ExceptBy(q2, selectorFunc)
|
||||
}
|
||||
Generated
Vendored
+17
-17
@@ -5,30 +5,30 @@ import "reflect"
|
||||
// Iterator is an alias for function to iterate over data.
|
||||
type Iterator func() (item interface{}, ok bool)
|
||||
|
||||
// Query is the type returned from query functions.
|
||||
// It can be iterated manually as shown in the example.
|
||||
// Query is the type returned from query functions. It can be iterated manually
|
||||
// as shown in the example.
|
||||
type Query struct {
|
||||
Iterate func() Iterator
|
||||
}
|
||||
|
||||
// KeyValue is a type that is used to iterate over a map
|
||||
// (if query is created from a map). This type is also used by
|
||||
// ToMap() method to output result of a query into a map.
|
||||
// KeyValue is a type that is used to iterate over a map (if query is created
|
||||
// from a map). This type is also used by ToMap() method to output result of a
|
||||
// query into a map.
|
||||
type KeyValue struct {
|
||||
Key interface{}
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// Iterable is an interface that has to be implemented by a
|
||||
// custom collection in order to work with linq.
|
||||
// Iterable is an interface that has to be implemented by a custom collection in
|
||||
// order to work with linq.
|
||||
type Iterable interface {
|
||||
Iterate() Iterator
|
||||
}
|
||||
|
||||
// From initializes a linq query with passed slice, array or map
|
||||
// as the source. String, channel or struct implementing Iterable
|
||||
// interface can be used as an input. In this case From delegates it
|
||||
// to FromString, FromChannel and FromIterable internally.
|
||||
// From initializes a linq query with passed slice, array or map as the source.
|
||||
// String, channel or struct implementing Iterable interface can be used as an
|
||||
// input. In this case From delegates it to FromString, FromChannel and
|
||||
// FromIterable internally.
|
||||
func From(source interface{}) Query {
|
||||
src := reflect.ValueOf(source)
|
||||
|
||||
@@ -84,8 +84,8 @@ func From(source interface{}) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// FromChannel initializes a linq query with passed channel,
|
||||
// linq iterates over channel until it is closed.
|
||||
// FromChannel initializes a linq query with passed channel, linq iterates over
|
||||
// channel until it is closed.
|
||||
func FromChannel(source <-chan interface{}) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -97,8 +97,8 @@ func FromChannel(source <-chan interface{}) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// FromString initializes a linq query with passed string,
|
||||
// linq iterates over runes of string.
|
||||
// FromString initializes a linq query with passed string, linq iterates over
|
||||
// runes of string.
|
||||
func FromString(source string) Query {
|
||||
runes := []rune(source)
|
||||
len := len(runes)
|
||||
@@ -120,8 +120,8 @@ func FromString(source string) Query {
|
||||
}
|
||||
}
|
||||
|
||||
// FromIterable initializes a linq query with custom collection passed.
|
||||
// This collection has to implement Iterable interface, linq iterates over items,
|
||||
// FromIterable initializes a linq query with custom collection passed. This
|
||||
// collection has to implement Iterable interface, linq iterates over items,
|
||||
// that has to implement Comparable interface or be basic types.
|
||||
func FromIterable(source Iterable) Query {
|
||||
return Query{
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package linq
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// genericType represents a any reflect.Type.
|
||||
type genericType int
|
||||
|
||||
var genericTp = reflect.TypeOf(new(genericType)).Elem()
|
||||
|
||||
// functionCache keeps genericFunc reflection objects in cache.
|
||||
type functionCache struct {
|
||||
MethodName string
|
||||
ParamName string
|
||||
FnValue reflect.Value
|
||||
FnType reflect.Type
|
||||
TypesIn []reflect.Type
|
||||
TypesOut []reflect.Type
|
||||
}
|
||||
|
||||
// genericFunc is a type used to validate and call dynamic functions.
|
||||
type genericFunc struct {
|
||||
Cache *functionCache
|
||||
}
|
||||
|
||||
// Call calls a dynamic function.
|
||||
func (g *genericFunc) Call(params ...interface{}) interface{} {
|
||||
paramsIn := make([]reflect.Value, len(params))
|
||||
for i, param := range params {
|
||||
paramsIn[i] = reflect.ValueOf(param)
|
||||
}
|
||||
paramsOut := g.Cache.FnValue.Call(paramsIn)
|
||||
if len(paramsOut) >= 1 {
|
||||
return paramsOut[0].Interface()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// newGenericFunc instantiates a new genericFunc pointer
|
||||
func newGenericFunc(methodName, paramName string, fn interface{}, validateFunc func(*functionCache) error) (*genericFunc, error) {
|
||||
cache := &functionCache{}
|
||||
cache.FnValue = reflect.ValueOf(fn)
|
||||
|
||||
if cache.FnValue.Kind() != reflect.Func {
|
||||
return nil, fmt.Errorf("%s: parameter [%s] is not a function type. It is a '%s'", methodName, paramName, cache.FnValue.Type())
|
||||
}
|
||||
cache.MethodName = methodName
|
||||
cache.ParamName = paramName
|
||||
cache.FnType = cache.FnValue.Type()
|
||||
numTypesIn := cache.FnType.NumIn()
|
||||
cache.TypesIn = make([]reflect.Type, numTypesIn)
|
||||
for i := 0; i < numTypesIn; i++ {
|
||||
cache.TypesIn[i] = cache.FnType.In(i)
|
||||
}
|
||||
|
||||
numTypesOut := cache.FnType.NumOut()
|
||||
cache.TypesOut = make([]reflect.Type, numTypesOut)
|
||||
for i := 0; i < numTypesOut; i++ {
|
||||
cache.TypesOut[i] = cache.FnType.Out(i)
|
||||
}
|
||||
if err := validateFunc(cache); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &genericFunc{Cache: cache}, nil
|
||||
}
|
||||
|
||||
// simpleParamValidator creates a function to validate genericFunc based in the
|
||||
// In and Out function parameters.
|
||||
func simpleParamValidator(In []reflect.Type, Out []reflect.Type) func(cache *functionCache) error {
|
||||
return func(cache *functionCache) error {
|
||||
var isValid = func() bool {
|
||||
if In != nil {
|
||||
if len(In) != len(cache.TypesIn) {
|
||||
return false
|
||||
}
|
||||
for i, paramIn := range In {
|
||||
if paramIn != genericTp && paramIn != cache.TypesIn[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
if Out != nil {
|
||||
if len(Out) != len(cache.TypesOut) {
|
||||
return false
|
||||
}
|
||||
for i, paramOut := range Out {
|
||||
if paramOut != genericTp && paramOut != cache.TypesOut[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if !isValid() {
|
||||
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))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// newElemTypeSlice creates a slice of items elem types.
|
||||
func newElemTypeSlice(items ...interface{}) []reflect.Type {
|
||||
typeList := make([]reflect.Type, len(items))
|
||||
for i, item := range items {
|
||||
typeItem := reflect.TypeOf(item)
|
||||
if typeItem.Kind() == reflect.Ptr {
|
||||
typeList[i] = typeItem.Elem()
|
||||
}
|
||||
}
|
||||
return typeList
|
||||
}
|
||||
|
||||
// formatFnSignature formats the func signature based in the parameters types.
|
||||
func formatFnSignature(In []reflect.Type, Out []reflect.Type) string {
|
||||
paramInNames := make([]string, len(In))
|
||||
for i, typeIn := range In {
|
||||
if typeIn == genericTp {
|
||||
paramInNames[i] = "T"
|
||||
} else {
|
||||
paramInNames[i] = typeIn.String()
|
||||
}
|
||||
|
||||
}
|
||||
paramOutNames := make([]string, len(Out))
|
||||
for i, typeOut := range Out {
|
||||
if typeOut == genericTp {
|
||||
paramOutNames[i] = "T"
|
||||
} else {
|
||||
paramOutNames[i] = typeOut.String()
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("func(%s)%s", strings.Join(paramInNames, ","), strings.Join(paramOutNames, ","))
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GroupByT is the typed version of GroupBy.
|
||||
//
|
||||
// - keySelectorFn is of type "func(TSource) TKey"
|
||||
// - elementSelectorFn is of type "func(TSource) TElement"
|
||||
//
|
||||
// NOTE: GroupBy has better performance than GroupByT.
|
||||
func (q Query) GroupByT(keySelectorFn interface{},
|
||||
elementSelectorFn interface{}) Query {
|
||||
keySelectorGenericFunc, err := newGenericFunc(
|
||||
"GroupByT", "keySelectorFn", keySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
keySelectorFunc := func(item interface{}) interface{} {
|
||||
return keySelectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
elementSelectorGenericFunc, err := newGenericFunc(
|
||||
"GroupByT", "elementSelectorFn", elementSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
elementSelectorFunc := func(item interface{}) interface{} {
|
||||
return elementSelectorGenericFunc.Call(item)
|
||||
|
||||
}
|
||||
|
||||
return q.GroupBy(keySelectorFunc, elementSelectorFunc)
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package linq
|
||||
|
||||
import "reflect"
|
||||
|
||||
// GroupJoin correlates the elements of two collections based on key equality,
|
||||
// and groups the results.
|
||||
//
|
||||
// This method produces hierarchical results, which means that elements from
|
||||
// outer query are paired with collections of matching elements from inner.
|
||||
// GroupJoin enables you to base your results on a whole set of matches for each
|
||||
// element of outer query.
|
||||
//
|
||||
// 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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// GroupJoinT is the typed version of GroupJoin.
|
||||
//
|
||||
// - inner: The query to join to the outer query.
|
||||
// - outerKeySelectorFn is of type "func(TOuter) TKey"
|
||||
// - innerKeySelectorFn is of type "func(TInner) TKey"
|
||||
// - resultSelectorFn: is of type "func(TOuter, inners []TInner) TResult"
|
||||
//
|
||||
// NOTE: GroupJoin has better performance than GroupJoinT.
|
||||
func (q Query) GroupJoinT(inner Query,
|
||||
outerKeySelectorFn interface{},
|
||||
innerKeySelectorFn interface{},
|
||||
resultSelectorFn interface{}) Query {
|
||||
outerKeySelectorGenericFunc, err := newGenericFunc(
|
||||
"GroupJoinT", "outerKeySelectorFn", outerKeySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
outerKeySelectorFunc := func(item interface{}) interface{} {
|
||||
return outerKeySelectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
innerKeySelectorFuncGenericFunc, err := newGenericFunc(
|
||||
"GroupJoinT", "innerKeySelectorFn", innerKeySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
innerKeySelectorFunc := func(item interface{}) interface{} {
|
||||
return innerKeySelectorFuncGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"GroupJoinT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(outer interface{}, inners []interface{}) interface{} {
|
||||
innerSliceType := reflect.MakeSlice(resultSelectorGenericFunc.Cache.TypesIn[1], 0, 0)
|
||||
innersSlicePointer := reflect.New(innerSliceType.Type())
|
||||
From(inners).ToSlice(innersSlicePointer.Interface())
|
||||
innersTyped := reflect.Indirect(innersSlicePointer).Interface()
|
||||
return resultSelectorGenericFunc.Call(outer, innersTyped)
|
||||
}
|
||||
|
||||
return q.GroupJoin(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc)
|
||||
}
|
||||
Generated
Vendored
+28
-8
@@ -2,8 +2,8 @@ package linq
|
||||
|
||||
// Intersect produces the set intersection of the source collection and the
|
||||
// provided input collection. The intersection of two sets A and B is defined as
|
||||
// the set that contains all the elements of A that also appear in B,
|
||||
// but no other elements.
|
||||
// the set that contains all the elements of A that also appear in B, but no
|
||||
// other elements.
|
||||
func (q Query) Intersect(q2 Query) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -31,14 +31,12 @@ func (q Query) Intersect(q2 Query) Query {
|
||||
|
||||
// IntersectBy produces the set intersection of the source collection and the
|
||||
// provided input collection. The intersection of two sets A and B is defined as
|
||||
// the set that contains all the elements of A that also appear in B,
|
||||
// but no other elements.
|
||||
// the set that contains all the elements of A that also appear in B, but no
|
||||
// other elements.
|
||||
//
|
||||
// IntersectBy invokes a transform function on each element of both collections.
|
||||
func (q Query) IntersectBy(
|
||||
q2 Query,
|
||||
selector func(interface{}) interface{},
|
||||
) Query {
|
||||
func (q Query) IntersectBy(q2 Query,
|
||||
selector func(interface{}) interface{}) Query {
|
||||
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
@@ -65,3 +63,25 @@ func (q Query) IntersectBy(
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// IntersectByT is the typed version of IntersectBy.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource) TSource"
|
||||
//
|
||||
// NOTE: IntersectBy has better performance than IntersectByT.
|
||||
func (q Query) IntersectByT(q2 Query,
|
||||
selectorFn interface{}) Query {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"IntersectByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return q.IntersectBy(q2, selectorFunc)
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// JoinT is the typed version of Join.
|
||||
//
|
||||
// - outerKeySelectorFn is of type "func(TOuter) TKey"
|
||||
// - innerKeySelectorFn is of type "func(TInner) TKey"
|
||||
// - resultSelectorFn is of type "func(TOuter,TInner) TResult"
|
||||
//
|
||||
// NOTE: Join has better performance than JoinT.
|
||||
func (q Query) JoinT(inner Query,
|
||||
outerKeySelectorFn interface{},
|
||||
innerKeySelectorFn interface{},
|
||||
resultSelectorFn interface{}) Query {
|
||||
outerKeySelectorGenericFunc, err := newGenericFunc(
|
||||
"JoinT", "outerKeySelectorFn", outerKeySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
outerKeySelectorFunc := func(item interface{}) interface{} {
|
||||
return outerKeySelectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
innerKeySelectorFuncGenericFunc, err := newGenericFunc(
|
||||
"JoinT", "innerKeySelectorFn",
|
||||
innerKeySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
innerKeySelectorFunc := func(item interface{}) interface{} {
|
||||
return innerKeySelectorFuncGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"JoinT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(outer interface{}, inner interface{}) interface{} {
|
||||
return resultSelectorGenericFunc.Call(outer, inner)
|
||||
}
|
||||
|
||||
return q.Join(inner, outerKeySelectorFunc, innerKeySelectorFunc, resultSelectorFunc)
|
||||
}
|
||||
+313
@@ -0,0 +1,313 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// OrderByT is the typed version of OrderBy.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource) TKey"
|
||||
//
|
||||
// NOTE: OrderBy has better performance than OrderByT.
|
||||
func (q Query) OrderByT(selectorFn interface{}) OrderedQuery {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"OrderByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return q.OrderBy(selectorFunc)
|
||||
}
|
||||
|
||||
// OrderByDescending sorts the elements of a collection in descending order.
|
||||
// Elements are sorted according to a key.
|
||||
func (q Query) OrderByDescending(selector func(interface{}) interface{}) OrderedQuery {
|
||||
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
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// OrderByDescendingT is the typed version of OrderByDescending.
|
||||
// - selectorFn is of type "func(TSource) TKey"
|
||||
// NOTE: OrderByDescending has better performance than OrderByDescendingT.
|
||||
func (q Query) OrderByDescendingT(selectorFn interface{}) OrderedQuery {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"OrderByDescendingT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return q.OrderByDescending(selectorFunc)
|
||||
}
|
||||
|
||||
// ThenBy performs a subsequent ordering of the elements in a collection in
|
||||
// ascending order. This method enables you to specify multiple sort criteria by
|
||||
// applying any number of ThenBy or ThenByDescending methods.
|
||||
func (oq OrderedQuery) ThenBy(
|
||||
selector func(interface{}) interface{}) OrderedQuery {
|
||||
return OrderedQuery{
|
||||
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
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ThenByT is the typed version of ThenBy.
|
||||
// - selectorFn is of type "func(TSource) TKey"
|
||||
// NOTE: ThenBy has better performance than ThenByT.
|
||||
func (oq OrderedQuery) ThenByT(selectorFn interface{}) OrderedQuery {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"ThenByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return oq.ThenBy(selectorFunc)
|
||||
}
|
||||
|
||||
// ThenByDescending performs a subsequent ordering of the elements in a
|
||||
// collection in descending order. This method enables you to specify multiple
|
||||
// sort criteria by applying any number of ThenBy or ThenByDescending methods.
|
||||
func (oq OrderedQuery) ThenByDescending(selector func(interface{}) interface{}) OrderedQuery {
|
||||
return OrderedQuery{
|
||||
orders: append(oq.orders, order{selector: selector, desc: true}),
|
||||
original: oq.original,
|
||||
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
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ThenByDescendingT is the typed version of ThenByDescending.
|
||||
// - selectorFn is of type "func(TSource) TKey"
|
||||
// NOTE: ThenByDescending has better performance than ThenByDescendingT.
|
||||
func (oq OrderedQuery) ThenByDescendingT(selectorFn interface{}) OrderedQuery {
|
||||
selectorFunc, ok := selectorFn.(func(interface{}) interface{})
|
||||
if !ok {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"ThenByDescending", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc = func(item interface{}) interface{} {
|
||||
return selectorGenericFunc.Call(item)
|
||||
}
|
||||
}
|
||||
return oq.ThenByDescending(selectorFunc)
|
||||
}
|
||||
|
||||
// Sort returns a new query by sorting elements with provided less function in
|
||||
// ascending order. The comparer function should return true if the parameter i
|
||||
// is less than j. While this method is uglier than chaining OrderBy,
|
||||
// OrderByDescending, ThenBy and ThenByDescending methods, it's performance is
|
||||
// much better.
|
||||
func (q Query) Sort(less func(i, j interface{}) bool) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SortT is the typed version of Sort.
|
||||
// - lessFn is of type "func(TSource,TSource) bool"
|
||||
// NOTE: Sort has better performance than SortT.
|
||||
func (q Query) SortT(lessFn interface{}) Query {
|
||||
lessGenericFunc, err := newGenericFunc(
|
||||
"SortT", "lessFn", lessFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
lessFunc := func(i, j interface{}) bool {
|
||||
return lessGenericFunc.Call(i, j).(bool)
|
||||
}
|
||||
|
||||
return q.Sort(lessFunc)
|
||||
}
|
||||
|
||||
type sorter struct {
|
||||
items []interface{}
|
||||
less func(i, j interface{}) bool
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
if len(r) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+658
@@ -0,0 +1,658 @@
|
||||
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
|
||||
}
|
||||
|
||||
// AllT is the typed version of All.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: All has better performance than AllT.
|
||||
func (q Query) AllT(predicateFn interface{}) bool {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"AllT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.All(predicateFunc)
|
||||
}
|
||||
|
||||
// Any determines whether any element of a collection exists.
|
||||
func (q Query) Any() bool {
|
||||
_, ok := q.Iterate()()
|
||||
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
|
||||
}
|
||||
|
||||
// AnyWithT is the typed version of AnyWith.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: AnyWith has better performance than AnyWithT.
|
||||
func (q Query) AnyWithT(predicateFn interface{}) bool {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"AnyWithT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.AnyWith(predicateFunc)
|
||||
}
|
||||
|
||||
// Average computes the average of a collection of numeric values.
|
||||
func (q Query) Average() (r float64) {
|
||||
next := q.Iterate()
|
||||
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
|
||||
}
|
||||
|
||||
// CountWithT is the typed version of CountWith.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: CountWith has better performance than CountWithT.
|
||||
func (q Query) CountWithT(predicateFn interface{}) int {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"CountWithT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.CountWith(predicateFunc)
|
||||
}
|
||||
|
||||
// First returns the first element of a collection.
|
||||
func (q Query) First() interface{} {
|
||||
item, _ := q.Iterate()()
|
||||
return item
|
||||
}
|
||||
|
||||
// FirstWith returns the first element of a collection that satisfies a
|
||||
// specified condition.
|
||||
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
|
||||
}
|
||||
|
||||
// FirstWithT is the typed version of FirstWith.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: FirstWith has better performance than FirstWithT.
|
||||
func (q Query) FirstWithT(predicateFn interface{}) interface{} {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"FirstWithT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.FirstWith(predicateFunc)
|
||||
}
|
||||
|
||||
// ForEach performs the specified action on each element of a collection.
|
||||
func (q Query) ForEach(action func(interface{})) {
|
||||
next := q.Iterate()
|
||||
|
||||
for item, ok := next(); ok; item, ok = next() {
|
||||
action(item)
|
||||
}
|
||||
}
|
||||
|
||||
// ForEachT is the typed version of ForEach.
|
||||
//
|
||||
// - actionFn is of type "func(TSource)"
|
||||
//
|
||||
// NOTE: ForEach has better performance than ForEachT.
|
||||
func (q Query) ForEachT(actionFn interface{}) {
|
||||
actionGenericFunc, err := newGenericFunc(
|
||||
"ForEachT", "actionFn", actionFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), nil),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
actionFunc := func(item interface{}) {
|
||||
actionGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
q.ForEach(actionFunc)
|
||||
}
|
||||
|
||||
// ForEachIndexed performs the specified action on each element of a collection.
|
||||
//
|
||||
// The first argument to action represents the zero-based index of that
|
||||
// element in the source collection. This can be useful if the elements are in a
|
||||
// known order and you want to do something with an element at a particular
|
||||
// index, for example. It can also be useful if you want to retrieve the index
|
||||
// of one or more elements. The second argument to action represents the
|
||||
// element to process.
|
||||
func (q Query) ForEachIndexed(action func(int, interface{})) {
|
||||
next := q.Iterate()
|
||||
index := 0
|
||||
|
||||
for item, ok := next(); ok; item, ok = next() {
|
||||
action(index, item)
|
||||
index++
|
||||
}
|
||||
}
|
||||
|
||||
// ForEachIndexedT is the typed version of ForEachIndexed.
|
||||
//
|
||||
// - actionFn is of type "func(int, TSource)"
|
||||
//
|
||||
// NOTE: ForEachIndexed has better performance than ForEachIndexedT.
|
||||
func (q Query) ForEachIndexedT(actionFn interface{}) {
|
||||
actionGenericFunc, err := newGenericFunc(
|
||||
"ForEachIndexedT", "actionFn", actionFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), nil),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
actionFunc := func(index int, item interface{}) {
|
||||
actionGenericFunc.Call(index, item)
|
||||
}
|
||||
|
||||
q.ForEachIndexed(actionFunc)
|
||||
}
|
||||
|
||||
// Last returns the last element of a collection.
|
||||
func (q Query) Last() (r interface{}) {
|
||||
next := q.Iterate()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// LastWithT is the typed version of LastWith.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: LastWith has better performance than LastWithT.
|
||||
func (q Query) LastWithT(predicateFn interface{}) interface{} {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"LastWithT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.LastWith(predicateFunc)
|
||||
}
|
||||
|
||||
// Max returns the maximum value in a collection of values.
|
||||
func (q Query) Max() (r interface{}) {
|
||||
next := q.Iterate()
|
||||
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
|
||||
}
|
||||
|
||||
// SingleWithT is the typed version of SingleWith.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource) bool"
|
||||
//
|
||||
// NOTE: SingleWith has better performance than SingleWithT.
|
||||
func (q Query) SingleWithT(predicateFn interface{}) interface{} {
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"SingleWithT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.SingleWith(predicateFunc)
|
||||
}
|
||||
|
||||
// SumInts computes the sum of a collection of numeric values.
|
||||
//
|
||||
// Values can be of any integer type: int, int8, int16, int32, int64. The result
|
||||
// is int64. Method returns zero if collection contains no elements.
|
||||
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. ToMap
|
||||
// doesn't empty the result map before populating it.
|
||||
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 the result map with
|
||||
// elements. Functions keySelector and valueSelector are executed for each
|
||||
// element of the collection to generate key and value for the map. Generated
|
||||
// key and value types must be assignable to the map's key and value types.
|
||||
// ToMapBy doesn't empty the result map before populating it.
|
||||
func (q Query) ToMapBy(result interface{},
|
||||
keySelector func(interface{}) interface{},
|
||||
valueSelector func(interface{}) interface{}) {
|
||||
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)
|
||||
}
|
||||
|
||||
// ToMapByT is the typed version of ToMapBy.
|
||||
//
|
||||
// - keySelectorFn is of type "func(TSource)TKey"
|
||||
// - valueSelectorFn is of type "func(TSource)TValue"
|
||||
//
|
||||
// NOTE: ToMapBy has better performance than ToMapByT.
|
||||
func (q Query) ToMapByT(result interface{},
|
||||
keySelectorFn interface{}, valueSelectorFn interface{}) {
|
||||
keySelectorGenericFunc, err := newGenericFunc(
|
||||
"ToMapByT", "keySelectorFn", keySelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
keySelectorFunc := func(item interface{}) interface{} {
|
||||
return keySelectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
valueSelectorGenericFunc, err := newGenericFunc(
|
||||
"ToMapByT", "valueSelectorFn", valueSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
valueSelectorFunc := func(item interface{}) interface{} {
|
||||
return valueSelectorGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
q.ToMapBy(result, keySelectorFunc, valueSelectorFunc)
|
||||
}
|
||||
|
||||
// ToSlice iterates over a collection and saves the results in the slice pointed
|
||||
// by v. It overwrites the existing slice, starting from index 0.
|
||||
//
|
||||
// If the slice pointed by v has sufficient capacity, v will be pointed to a
|
||||
// resliced slice. If it does not, a new underlying array will be allocated and
|
||||
// v will point to it.
|
||||
func (q Query) ToSlice(v interface{}) {
|
||||
res := reflect.ValueOf(v)
|
||||
slice := reflect.Indirect(res)
|
||||
|
||||
cap := slice.Cap()
|
||||
res.Elem().Set(slice.Slice(0, cap)) // make len(slice)==cap(slice) from now on
|
||||
|
||||
next := q.Iterate()
|
||||
index := 0
|
||||
for item, ok := next(); ok; item, ok = next() {
|
||||
if index >= cap {
|
||||
slice, cap = grow(slice)
|
||||
}
|
||||
slice.Index(index).Set(reflect.ValueOf(item))
|
||||
index++
|
||||
}
|
||||
|
||||
// reslice the len(res)==cap(res) actual res size
|
||||
res.Elem().Set(slice.Slice(0, index))
|
||||
}
|
||||
|
||||
// grow grows the slice s by doubling its capacity, then it returns the new
|
||||
// slice (resliced to its full capacity) and the new capacity.
|
||||
func grow(s reflect.Value) (v reflect.Value, newCap int) {
|
||||
cap := s.Cap()
|
||||
if cap == 0 {
|
||||
cap = 1
|
||||
} else {
|
||||
cap *= 2
|
||||
}
|
||||
newSlice := reflect.MakeSlice(s.Type(), cap, cap)
|
||||
reflect.Copy(newSlice, s)
|
||||
return newSlice, cap
|
||||
}
|
||||
Generated
Vendored
+3
-3
@@ -2,9 +2,9 @@ package linq
|
||||
|
||||
// Reverse inverts the order of the elements in a collection.
|
||||
//
|
||||
// Unlike OrderBy, this sorting method does not consider the actual values themselves
|
||||
// in determining the order. Rather, it just returns the elements in the reverse order
|
||||
// from which they are produced by the underlying source.
|
||||
// Unlike OrderBy, this sorting method does not consider the actual values
|
||||
// themselves in determining the order. Rather, it just returns the elements in
|
||||
// the reverse order from which they are produced by the underlying source.
|
||||
func (q Query) Reverse() Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SelectT is the typed version of Select.
|
||||
// - selectorFn is of type "func(TSource)TResult"
|
||||
// NOTE: Select has better performance than SelectT.
|
||||
func (q Query) SelectT(selectorFn interface{}) Query {
|
||||
|
||||
selectGenericFunc, err := newGenericFunc(
|
||||
"SelectT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(item interface{}) interface{} {
|
||||
return selectGenericFunc.Call(item)
|
||||
}
|
||||
|
||||
return q.Select(selectorFunc)
|
||||
}
|
||||
|
||||
// SelectIndexed projects each element of a collection into a new form by
|
||||
// incorporating the element's index. Returns a query with the result of
|
||||
// invoking the transform function on each element of original source.
|
||||
//
|
||||
// The first argument to selector represents the zero-based index of that
|
||||
// element in the source collection. This can be useful if the elements are in a
|
||||
// known order and you want to do something with an element at a particular
|
||||
// index, for example. It can also be useful if you want to retrieve the index
|
||||
// of one or more elements. The second argument to selector represents the
|
||||
// element to process.
|
||||
//
|
||||
// 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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SelectIndexedT is the typed version of SelectIndexed.
|
||||
// - selectorFn is of type "func(int,TSource)TResult"
|
||||
// NOTE: SelectIndexed has better performance than SelectIndexedT.
|
||||
func (q Query) SelectIndexedT(selectorFn interface{}) Query {
|
||||
selectGenericFunc, err := newGenericFunc(
|
||||
"SelectIndexedT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(index int, item interface{}) interface{} {
|
||||
return selectGenericFunc.Call(index, item)
|
||||
}
|
||||
|
||||
return q.SelectIndexed(selectorFunc)
|
||||
}
|
||||
+265
@@ -0,0 +1,265 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SelectManyT is the typed version of SelectMany.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource)Query"
|
||||
//
|
||||
// NOTE: SelectMany has better performance than SelectManyT.
|
||||
func (q Query) SelectManyT(selectorFn interface{}) Query {
|
||||
|
||||
selectManyGenericFunc, err := newGenericFunc(
|
||||
"SelectManyT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(Query))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(inner interface{}) Query {
|
||||
return selectManyGenericFunc.Call(inner).(Query)
|
||||
}
|
||||
return q.SelectMany(selectorFunc)
|
||||
|
||||
}
|
||||
|
||||
// SelectManyIndexed projects each element of a collection to a Query, iterates
|
||||
// and flattens the resulting collection into one collection.
|
||||
//
|
||||
// The first argument to selector represents the zero-based index of that
|
||||
// element in the source collection. This can be useful if the elements are in a
|
||||
// known order and you want to do something with an element at a particular
|
||||
// index, for example. It can also be useful if you want to retrieve the index
|
||||
// of one or more elements. The second argument to selector represents the
|
||||
// element to process.
|
||||
func (q Query) SelectManyIndexed(selector func(int, interface{}) Query) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SelectManyIndexedT is the typed version of SelectManyIndexed.
|
||||
//
|
||||
// - selectorFn is of type "func(int,TSource)Query"
|
||||
//
|
||||
// NOTE: SelectManyIndexed has better performance than SelectManyIndexedT.
|
||||
func (q Query) SelectManyIndexedT(selectorFn interface{}) Query {
|
||||
|
||||
selectManyIndexedGenericFunc, err := newGenericFunc(
|
||||
"SelectManyIndexedT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(Query))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(index int, inner interface{}) Query {
|
||||
return selectManyIndexedGenericFunc.Call(index, inner).(Query)
|
||||
}
|
||||
|
||||
return q.SelectManyIndexed(selectorFunc)
|
||||
}
|
||||
|
||||
// SelectManyBy projects each element of a collection to a Query, iterates and
|
||||
// flattens the resulting collection into one collection, and invokes a result
|
||||
// selector function on each element therein.
|
||||
func (q Query) SelectManyBy(selector func(interface{}) Query,
|
||||
resultSelector func(interface{}, interface{}) interface{}) Query {
|
||||
|
||||
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(item, outer)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SelectManyByT is the typed version of SelectManyBy.
|
||||
//
|
||||
// - selectorFn is of type "func(TSource)Query"
|
||||
// - resultSelectorFn is of type "func(TSource,TCollection)TResult"
|
||||
//
|
||||
// NOTE: SelectManyBy has better performance than SelectManyByT.
|
||||
func (q Query) SelectManyByT(selectorFn interface{},
|
||||
resultSelectorFn interface{}) Query {
|
||||
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"SelectManyByT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(Query))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(outer interface{}) Query {
|
||||
return selectorGenericFunc.Call(outer).(Query)
|
||||
}
|
||||
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"SelectManyByT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(outer interface{}, item interface{}) interface{} {
|
||||
return resultSelectorGenericFunc.Call(outer, item)
|
||||
}
|
||||
|
||||
return q.SelectManyBy(selectorFunc, resultSelectorFunc)
|
||||
}
|
||||
|
||||
// SelectManyByIndexed projects each element of a collection to a Query,
|
||||
// iterates and flattens the resulting collection into one collection, and
|
||||
// invokes a result selector function on each element therein. The index of each
|
||||
// source element is used in the intermediate projected form of that element.
|
||||
func (q Query) SelectManyByIndexed(selector func(int, interface{}) Query,
|
||||
resultSelector func(interface{}, interface{}) interface{}) Query {
|
||||
|
||||
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(item, outer)
|
||||
return
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SelectManyByIndexedT is the typed version of SelectManyByIndexed.
|
||||
//
|
||||
// - selectorFn is of type "func(int,TSource)Query"
|
||||
// - resultSelectorFn is of type "func(TSource,TCollection)TResult"
|
||||
//
|
||||
// NOTE: SelectManyByIndexed has better performance than
|
||||
// SelectManyByIndexedT.
|
||||
func (q Query) SelectManyByIndexedT(selectorFn interface{},
|
||||
resultSelectorFn interface{}) Query {
|
||||
selectorGenericFunc, err := newGenericFunc(
|
||||
"SelectManyByIndexedT", "selectorFn", selectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(Query))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
selectorFunc := func(index int, outer interface{}) Query {
|
||||
return selectorGenericFunc.Call(index, outer).(Query)
|
||||
}
|
||||
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"SelectManyByIndexedT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(outer interface{}, item interface{}) interface{} {
|
||||
return resultSelectorGenericFunc.Call(outer, item)
|
||||
}
|
||||
|
||||
return q.SelectManyByIndexed(selectorFunc, resultSelectorFunc)
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
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()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SkipWhileT is the typed version of SkipWhile.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource)bool"
|
||||
//
|
||||
// NOTE: SkipWhile has better performance than SkipWhileT.
|
||||
func (q Query) SkipWhileT(predicateFn interface{}) Query {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"SkipWhileT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.SkipWhile(predicateFunc)
|
||||
}
|
||||
|
||||
// SkipWhileIndexed bypasses elements in a collection as long as a specified
|
||||
// condition is true and then returns the remaining elements. The element's
|
||||
// index is used in the logic of the predicate function.
|
||||
//
|
||||
// This method tests each element by using predicate and skips the element if
|
||||
// the result is true. After the predicate function returns false for an
|
||||
// element, that element and the remaining elements in source are returned and
|
||||
// there are no more invocations of predicate.
|
||||
func (q Query) SkipWhileIndexed(predicate func(int, interface{}) bool) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
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()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SkipWhileIndexedT is the typed version of SkipWhileIndexed.
|
||||
//
|
||||
// - predicateFn is of type "func(int,TSource)bool"
|
||||
//
|
||||
// NOTE: SkipWhileIndexed has better performance than SkipWhileIndexedT.
|
||||
func (q Query) SkipWhileIndexedT(predicateFn interface{}) Query {
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"SkipWhileIndexedT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(index int, item interface{}) bool {
|
||||
return predicateGenericFunc.Call(index, item).(bool)
|
||||
}
|
||||
|
||||
return q.SkipWhileIndexed(predicateFunc)
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TakeWhileT is the typed version of TakeWhile.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource)bool"
|
||||
//
|
||||
// NOTE: TakeWhile has better performance than TakeWhileT.
|
||||
func (q Query) TakeWhileT(predicateFn interface{}) Query {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"TakeWhileT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.TakeWhile(predicateFunc)
|
||||
}
|
||||
|
||||
// TakeWhileIndexed returns elements from a collection as long as a specified
|
||||
// condition is true. The element's index is used in the logic of the predicate
|
||||
// function. The first argument of predicate represents the zero-based index of
|
||||
// the element within collection. The second argument represents the element to
|
||||
// test.
|
||||
func (q Query) TakeWhileIndexed(predicate func(int, interface{}) bool) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// TakeWhileIndexedT is the typed version of TakeWhileIndexed.
|
||||
//
|
||||
// - predicateFn is of type "func(int,TSource)bool"
|
||||
//
|
||||
// NOTE: TakeWhileIndexed has better performance than TakeWhileIndexedT.
|
||||
func (q Query) TakeWhileIndexedT(predicateFn interface{}) Query {
|
||||
whereFunc, err := newGenericFunc(
|
||||
"TakeWhileIndexedT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(index int, item interface{}) bool {
|
||||
return whereFunc.Call(index, item).(bool)
|
||||
}
|
||||
|
||||
return q.TakeWhileIndexed(predicateFunc)
|
||||
}
|
||||
Generated
Vendored
+3
-4
@@ -2,10 +2,9 @@ package linq
|
||||
|
||||
// Union produces the set union of two collections.
|
||||
//
|
||||
// This method excludes duplicates from the return set.
|
||||
// This is different behavior to the Concat method,
|
||||
// which returns all the elements in the input collection
|
||||
// including duplicates.
|
||||
// This method excludes duplicates from the return set. This is different
|
||||
// behavior to the Concat method, which returns all the elements in the input
|
||||
// collection including duplicates.
|
||||
func (q Query) Union(q2 Query) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WhereT is the typed version of Where.
|
||||
//
|
||||
// - predicateFn is of type "func(TSource)bool"
|
||||
//
|
||||
// NOTE: Where has better performance than WhereT.
|
||||
func (q Query) WhereT(predicateFn interface{}) Query {
|
||||
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"WhereT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(item interface{}) bool {
|
||||
return predicateGenericFunc.Call(item).(bool)
|
||||
}
|
||||
|
||||
return q.Where(predicateFunc)
|
||||
}
|
||||
|
||||
// WhereIndexed filters a collection of values based on a predicate. Each
|
||||
// element's index is used in the logic of the predicate function.
|
||||
//
|
||||
// The first argument represents the zero-based index of the element within
|
||||
// collection. The second argument of predicate represents the element to test.
|
||||
func (q Query) WhereIndexed(predicate func(int, interface{}) bool) Query {
|
||||
return Query{
|
||||
Iterate: func() Iterator {
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// WhereIndexedT is the typed version of WhereIndexed.
|
||||
//
|
||||
// - predicateFn is of type "func(int,TSource)bool"
|
||||
//
|
||||
// NOTE: WhereIndexed has better performance than WhereIndexedT.
|
||||
func (q Query) WhereIndexedT(predicateFn interface{}) Query {
|
||||
predicateGenericFunc, err := newGenericFunc(
|
||||
"WhereIndexedT", "predicateFn", predicateFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(int), new(genericType)), newElemTypeSlice(new(bool))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
predicateFunc := func(index int, item interface{}) bool {
|
||||
return predicateGenericFunc.Call(index, item).(bool)
|
||||
}
|
||||
|
||||
return q.WhereIndexed(predicateFunc)
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ZipT is the typed version of Zip.
|
||||
//
|
||||
// - resultSelectorFn is of type "func(TFirst,TSecond)TResult"
|
||||
//
|
||||
// NOTE: Zip has better performance than ZipT.
|
||||
func (q Query) ZipT(q2 Query,
|
||||
resultSelectorFn interface{}) Query {
|
||||
resultSelectorGenericFunc, err := newGenericFunc(
|
||||
"ZipT", "resultSelectorFn", resultSelectorFn,
|
||||
simpleParamValidator(newElemTypeSlice(new(genericType), new(genericType)), newElemTypeSlice(new(genericType))),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
resultSelectorFunc := func(item1 interface{}, item2 interface{}) interface{} {
|
||||
return resultSelectorGenericFunc.Call(item1, item2)
|
||||
}
|
||||
|
||||
return q.Zip(q2, resultSelectorFunc)
|
||||
}
|
||||
Reference in New Issue
Block a user