feat(Go-Tool):2020/11/23:修改项目案例(按照一定规则对一组数据进行排序分组)

This commit is contained in:
Huangzj
2020-11-23 11:00:36 +08:00
parent 179e5f6a31
commit 333d7a5d66
47 changed files with 2851 additions and 1469 deletions
+2 -2
View File
@@ -8,7 +8,7 @@ package ProgressTest
import (
"Go-Tool/util/file"
"Go-Tool/util/jsonEnhance"
"encoding/json"
)
type DeliverList struct {
@@ -24,6 +24,6 @@ type Deliver struct {
func MakeData() DeliverList {
var deliver DeliverList
content, _ := file.ReadJsonFile("F:\\Go_BySelf\\src\\Go-Tool\\ProgressTest\\deliverData.json")
jsonEnhance.UnmarshalFromString(content, &deliver)
_ = json.Unmarshal([]byte(content), &deliver)
return deliver
}
+30 -29
View File
@@ -9,13 +9,14 @@ package ProgressTest
import (
"Go-Tool/util/rand"
"fmt"
_ "github.com/ahmetb/go-linq"
"math"
"testing"
)
const (
MinGroupNum = 10
MaxGroupNum = 80
MaxGroupNum = 30
)
//需求(直接举例)
@@ -26,22 +27,16 @@ const (
//第四步:40每组的数据,要分成最后的10 * 4 的4组,按照每次取前5再随机取5个组成一组的方式组成4组
//第五步:不足4组的,按照取前5和随机5个的方式进行分组
func TestDeliver2(t *testing.T) {
firstOrderRule = FirstOrderRule
secondOrderRule = SecondOrderRule
thirdOrderRule = ThirdOrderRule
list := MakeData() //创建数据
resultList := deliver2(list.Deliver) //进行分组操作
dataList := MakeData() //创建数据
resultList := deliver2(dataList.Deliver) //进行分组操作
fmt.Printf(fmt.Sprintf("%v", resultList))
}
func deliver2(delivers []*Deliver) [][]int {
resultList := make([][]int, 0)
newDelivers := make([]*Deliver, 0)
//获得第一次按照大区间分组后剩下的元素数量、第一次分组所有组、分到的大组数量
leaveNum, partitions, partition := FirstOrder(delivers, newDelivers)
leaveNum, partitions, partition, newDelivers := FirstOrder(delivers, newDelivers)
resultList = withCompletePartition(partitions, resultList) //对完整的分组进行处理
resultList = withLeavePartition(resultList, newDelivers, leaveNum, partition) //对剩余的分组进行处理
@@ -63,7 +58,7 @@ func withCompletePartition(partitions [][]*Deliver, resultList [][]int) [][]int
return resultList
}
func FirstOrder(delivers []*Deliver, newDelivers []*Deliver) (int, [][]*Deliver, int) {
func FirstOrder(delivers []*Deliver, newDelivers []*Deliver) (int, [][]*Deliver, int, []*Deliver) {
newDelivers = firstOrderRule(delivers) //第一次排序
partition := len(newDelivers) / MaxGroupNum
leaveNum := len(newDelivers) % MaxGroupNum
@@ -72,25 +67,29 @@ func FirstOrder(delivers []*Deliver, newDelivers []*Deliver) (int, [][]*Deliver,
for i := 0; i < partition; i++ {
partitions = append(partitions, newDelivers[i*MaxGroupNum:(i+1)*MaxGroupNum])
}
return leaveNum, partitions, partition
return leaveNum, partitions, partition, newDelivers
}
func secondOrder(delivers []*Deliver, resultList [][]int, listNum int) [][]int {
newDelivers := secondOrderRule(delivers) //第二次排序
if listNum > 4*MinGroupNum {
fourInOne := listNum / (4 * MinGroupNum)
leave := listNum % (4 * MinGroupNum)
for i := 0; i < fourInOne; i++ {
resultList = append(resultList, calculateFourInOne(newDelivers[i*4*MinGroupNum:(i+1)*4*MinGroupNum])...) //四倍在一组的计算
}
if leave != 0 {
resultList = append(resultList, calculateByLeft(toList(newDelivers[fourInOne*4*MinGroupNum:]))...)
}
resultList = moreThenFour(listNum, resultList, newDelivers) //大于4倍区间的处理
} else {
resultList = append(resultList, calculateByLeft(toList(newDelivers))...)
resultList = append(resultList, calculateByLeft(thirdOrderRule(newDelivers))...)
}
return resultList
}
func moreThenFour(listNum int, resultList [][]int, newDelivers []*Deliver) [][]int {
fourInOne := listNum / (4 * MinGroupNum)
leave := listNum % (4 * MinGroupNum)
for i := 0; i < fourInOne; i++ {
resultList = append(resultList, calculateByLeft(thirdOrderRule(newDelivers[i*4*MinGroupNum:(i+1)*4*MinGroupNum]))...) //四倍在一组的计算
}
if leave != 0 {
resultList = append(resultList, calculateByLeft(thirdOrderRule(newDelivers[fourInOne*4*MinGroupNum:]))...)
}
return resultList
}
@@ -103,12 +102,6 @@ func toList(deliver []*Deliver) [][]int {
return result
}
func calculateFourInOne(delivers []*Deliver) [][]int {
Ids := thirdOrderRule(delivers) //第三次排序
result := calculateByLeft(Ids)
return result
}
func calculateByLeft(leftList [][]int) [][]int {
result := make([][]int, 0)
for len(leftList) != 0 {
@@ -138,3 +131,11 @@ func getFirstSome(list [][]int) []int {
func getFrontAndEnd(leftList [][]int) ([]int, [][]int) {
return rand.GetAwardByWeightWithLeftAward(leftList)
}
func init() {
//初始化排序规则
firstOrderRule = FirstOrderRule
secondOrderRule = SecondOrderRule
thirdOrderRule = ThirdOrderRule
}
+2 -2
View File
@@ -1,7 +1,7 @@
/*
* @Author : huangzj
* @Time : 2020/11/13 12:00
* @Description:在工程中有一个需求是对 指定数量的数组进行划分,按照每一层的规则分成对应数量,这边做一个测试.
* @Description:在工程中有一个需求是对 指定数量的数组进行划分,按照每一层的规则分成对应数量,这边做一个测试.(这种方式写的代码太麻烦了,直接废弃,后面有机会再弄)
*/
package ProgressTest
@@ -9,7 +9,7 @@ package ProgressTest
import (
"Go-Tool/util/rand"
"fmt"
"github.com/ahmetalpbalkan/go-linq"
"github.com/ahmetb/go-linq"
"testing"
)
+1 -1
View File
@@ -6,7 +6,7 @@
package ProgressTest
import "github.com/ahmetalpbalkan/go-linq"
import "github.com/ahmetb/go-linq"
type orderRule = func([]*Deliver) []*Deliver
type intOrderRule = func([]*Deliver) [][]int
+1
View File
@@ -58,6 +58,7 @@
2020/11/16:新增项目案例(按照一定规则对一组数据进行排序分组)
2020/11/23:修改项目案例(按照一定规则对一组数据进行排序分组)
# mod vendor模式加载包
通过go mod的方式加载的github上面的包会有报红的问题,但是包本身是可以运行的,这样就是会有一个问题,如果你想要点击去看方法的内容,没办法做到
-14
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
},
}
}
@@ -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
View File
@@ -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.
@@ -1,16 +1,21 @@
# go-linq [![GoDoc](https://godoc.org/github.com/ahmetalpbalkan/go-linq?status.svg)](https://godoc.org/github.com/ahmetalpbalkan/go-linq) [![Build Status](https://travis-ci.org/ahmetalpbalkan/go-linq.svg?branch=master)](https://travis-ci.org/ahmetalpbalkan/go-linq) [![Coverage Status](https://coveralls.io/repos/github/ahmetalpbalkan/go-linq/badge.svg?branch=master)](https://coveralls.io/github/ahmetalpbalkan/go-linq?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ahmetalpbalkan/go-linq)](https://goreportcard.com/report/github.com/ahmetalpbalkan/go-linq)
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
View File
@@ -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)
}
@@ -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
}
@@ -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 {
@@ -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
View File
@@ -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
View File
@@ -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)
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
@@ -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
View File
@@ -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
View File
@@ -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)
}