当前位置: 首页 > 工具软件 > Go-LINQ > 使用案例 >

Go-Linq包方法分析

蔡晨
2023-12-01

注:以下所有的方法来自于 https://github.com/ahmetb/go-linq,只对对应方法进行用法分析.

具体的使用示例可以参考 https://godoc.org/github.com/ahmetb/go-linq#

目录

Ⅰ.前置:结构体与数据构造方法

Ⅱ.方法使用解析

1、Aggregate:自定义聚合操作

2、All:判断是否所有元素都满足条件

3、Any:判断是否有任意个元素满足条件

4、Where:根据条件查询对应所有元素

5、计算类型的函数

6、组合两个数组,或者添加元素

7、Contains:判断元素是否在Query中

8、First、Last:第一个最后一个

9、Distinct:过滤数组使所有元素唯一

10、Except:求差集

11、GroupBy:分组

12、Intersect:求交集

13、OrderBy:排序

14、Skip:略过满足指定条件的元素

15、Count:计算数组长度

16、ToMap:转换为Map

17、ToSlice:转换为数组

18、Take:提取对应数量的元素

19、Select:筛选结构体字段/规则操作

20、SelectMany:扁平化多维数组

21、Join:关联结构体

Ⅲ.注意事项


Ⅰ.前置:结构体与数据构造方法

type Book struct {
	Name        string
	Author      string
	Money       float64
	WordsNum    int
	PublishTime time.Time
}


type Person struct {
	Name string //拥有者的名字
}

type Pet struct {
	Name      string //动物的名字
	OwnerName string //拥有者的名字
}

type Man struct {
	Name string //拥有者的名字
	Pets []Pets //宠物们
}

type Pets struct {
	Name string //动物的名字
}
func MakeInnerData() []Man {
	man := make([]Man, 0)
	man = append(man, Man{
		Name: "康康",
		Pets: []Pets{{Name: "康康的狗"}, {Name: "康康的猫"}},
	})
	man = append(man, Man{
		Name: "老施",
		Pets: []Pets{{Name: "老施的"}, {Name: "老施的鸟"}},
	})
	man = append(man, Man{
		Name: "小明",
		Pets: []Pets{{Name: "小明的"}, {Name: "小明的狗"}},
	})

	return man
}

func MakeJoinData() ([]Person, []Pet) {
	kangkang := Person{Name: "爱吃合合乐的康康"}
	laoshi := Person{Name: "老施"}
	xiaoming := Person{Name: "不要催-小明"}
	expect := Person{Name: "我是没有宠物的人"}

	dog := Pet{Name: "康康的狗", OwnerName: kangkang.Name}
	cat := Pet{Name: "康康的猫", OwnerName: kangkang.Name}
	fish := Pet{Name: "老施的", OwnerName: laoshi.Name}
	pig := Pet{Name: "小明的", OwnerName: xiaoming.Name}

	return []Person{kangkang, laoshi, xiaoming, expect}, []Pet{dog, cat, fish, pig}
}




func MakeBook() []Book {
	bookList := make([]Book, 0)
	bookList = append(bookList, Book{
		Name:        "Go语言",
		Author:      "Go",
		Money:       100,
		WordsNum:    1000,
		PublishTime: time.Date(2020, 1, 1, 10, 0, 0, 0, time.Local),
	})
	bookList = append(bookList, Book{
		Name:        "Effective Java",
		Author:      "Java",
		Money:       78,
		WordsNum:    9000,
		PublishTime: time.Date(2020, 2, 15, 10, 0, 0, 0, time.Local),
	})
	bookList = append(bookList, Book{
		Name:        "Java语言",
		Author:      "Java",
		Money:       50,
		WordsNum:    3000,
		PublishTime: time.Date(2020, 2, 1, 10, 0, 0, 0, time.Local),
	})
	bookList = append(bookList, Book{
		Name:        "Lua语言",
		Author:      "Lua",
		Money:       75,
		WordsNum:    45000,
		PublishTime: time.Date(2020, 1, 10, 10, 0, 0, 0, time.Local),
	})
	bookList = append(bookList, Book{
		Name:        "React语言",
		Author:      "React",
		Money:       99,
		WordsNum:    14500,
		PublishTime: time.Date(2020, 7, 1, 10, 0, 0, 0, time.Local),
	})
	bookList = append(bookList, Book{
		Name:        "Red语言",
		Author:      "Red",
		Money:       28,
		WordsNum:    880,
		PublishTime: time.Date(2019, 4, 1, 10, 0, 0, 0, time.Local),
	})
	bookList = append(bookList, Book{
		Name:        "JavaScript语言",
		Author:      "JavaScript",
		Money:       81,
		WordsNum:    3776,
		PublishTime: time.Date(2019, 5, 17, 10, 0, 0, 0, time.Local),
	})
	return bookList
}

func MakeBook1() []Book {
	bookList := make([]Book, 0)
	bookList = append(bookList, Book{
		Name:        "Go语言",
		Author:      "Go",
		Money:       100,
		WordsNum:    1000,
		PublishTime: time.Date(2020, 1, 1, 10, 0, 0, 0, time.Local),
	})
	bookList = append(bookList, Book{
		Name:        "Effective Java",
		Author:      "Java",
		Money:       78,
		WordsNum:    9000,
		PublishTime: time.Date(2020, 2, 15, 10, 0, 0, 0, time.Local),
	})
	bookList = append(bookList, Book{
		Name:        "Go语言(第二版)",
		Author:      "Go",
		Money:       50,
		WordsNum:    3000,
		PublishTime: time.Date(2020, 2, 1, 10, 0, 0, 0, time.Local),
	})
	return bookList
}
func MakeBook2() []Book {
	bookList := make([]Book, 0)
	bookList = append(bookList, Book{
		Name:        "Go语言",
		Author:      "Go",
		Money:       100,
		WordsNum:    1000,
		PublishTime: time.Date(2020, 1, 1, 10, 0, 0, 0, time.Local),
	})
	return bookList
}

func MakeBook3() []Book {
	bookList := make([]Book, 0)
	bookList = append(bookList, Book{
		Name:        "Go语言",
		Author:      "Go",
		Money:       100,
		WordsNum:    1000,
		PublishTime: time.Date(2020, 1, 1, 10, 0, 0, 0, time.Local),
	})
	bookList = append(bookList, Book{
		Name:        "Go语言(第三版)",
		Author:      "Go",
		Money:       50,
		WordsNum:    3000,
		PublishTime: time.Date(2020, 2, 1, 10, 0, 0, 0, time.Local),
	})
	return bookList
}

 

Ⅱ.方法使用解析

1、Aggregate:自定义聚合操作

自定义聚合方法,提供了两个变式,一个是支持固定类型的 AggregateT 方法,一个是支持interface的 Aggregate 方法。

Aggregate提供了一个方法,支持前后两个参数:

func (q Query) Aggregate(f func(interface{}, interface{}) interface{}) interface{} 

该方法就是聚合操作的具体方法,这边的使用例子如下:

func Aggregate() {
	bookList := MakeBook()
	query := linq.From(bookList)
	// 这边的聚合操作其实就是【自定义对应的比较方法】。
	// 根据注释,第一次拿到的是数组的第一个元素,然后跟第二个元素进行比较,如果第二个元素符合条件则【替换第一个元素】,否则当前聚合操作还是持有第一个元素。
	// 以此类推后面的元素
	result := query.Aggregate(AggregateFunc).(Book)
	fmt.Println(fmt.Sprintf("聚合操作查询出的结果是【%s】这本书,它的发布时间最早为:%v", result.Name, timed.GetTimeDefaultFormatString(result.PublishTime.Unix())))
}


//自定义聚合操作方法
var AggregateFunc = func(first interface{}, second interface{}) interface{} {
	if first.(Book).PublishTime.Before(second.(Book).PublishTime) {
		return first
	}
	return second
}

AggregateT 方法支持一个自定义类型的入参,如下:

// 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{} 

类似的除了方法自定义之外,这边也是支持两个入参,具体使用例子类似上面

 

2、All:判断是否所有元素都满足条件

All提供两个方法

              一个是支持具体类型的 AllT 方法,

              一个是支持interface的All方法,入参是一个predicate的条件方法,即返回是否所有元素都满足条件的结果,true或者false

// All determines whether all elements of a collection satisfy a condition.
func (q Query) All(predicate func(interface{}) bool) bool 

这边的使用例子是:

func All() {
	bookList := MakeBook()
	query := linq.From(bookList)
	result := query.All(PublishTimeBeforeFunc)
	fmt.Println(fmt.Sprintf("是否所有的书的发表时间都早于2020年1月1号,答案是%v", result))

	result = query.All(PublishTimeAfterFunc)
	fmt.Println(fmt.Sprintf("是否所有的书的发表时间都晚于2018年1月1号,答案是%v", result))
}

3、Any:判断是否有任意个元素满足条件

这边提供了三个方法,Any、AnyWith、AnyWithT

                            Any方法返回元素是否存在的结果(true、false)

                            AnyWith和AnyWithT,根据传入的判断条件,返回满足判断条件的元素是否大于等于1的结果(true、false)

具体的使用例子如下:

func AnyWith() {
	bookList := MakeBook()
	query := linq.From(bookList)
	result := query.AnyWith(PublishTimeBeforeFunc) //判断是否有任意个元素满足条件
	fmt.Println(fmt.Sprintf("是否有任意书的发表时间早于2020年1月1号,答案是%v", result))

	result = query.AnyWith(PublishTimeAfterFunc2) //判断是否有任意个元素满足条件
	fmt.Println(fmt.Sprintf("是否有任意书的发表时间晚于2021年1月1号,答案是%v", result))
}

4、Where:根据条件查询对应所有元素

Where方法是通过条件判断,查询到所有满足条件的元素。这边也是提供了Where和WhereT两个方法,具体的使用例子如下:

func Where() {
	bookList := MakeBook()
	query := linq.From(bookList)
	fmt.Println()
	fmt.Println()
	query.WhereT(func(book Book) bool {
		return book.Money > float64(70)
	}).ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("作者%v,写了一本书叫做%v", book.Author, book.Name))
	})
}

5、计算类型的函数

计算类型的函数,这边主要介绍的是 Average:求平均值 、 Max:求最大值、 Min:求最小值,传入的类型必须是 number类型的,否则会报错,因为具体的处理也是通过switch操作.无法处理非number的类型,具体的使用例子如下:

func Calculate() {
	query := linq.From([]int{1, 2, 3, 4, 5, 6, 7, 8, 9})
	fmt.Println(fmt.Sprintf("平均值为:%f", query.Average()))
	fmt.Println(fmt.Sprintf("最大的值为%d", query.Max()))
	fmt.Println(fmt.Sprintf("最小的值为%d", query.Min()))
}

6、组合两个数组,或者添加元素

组合两个数组这边提供两个方法:

                   Concat:会排除掉两个Query中重复的元素

                   Union: 不会排除两个Query中重复的元素,有多少个元素就组合成多少个元素的Query

添加元素这边也提供了两个方法:

                   Append: 将新的元素添加到Query的最后一个位置

                   PrePend:将新的元素添加到Query的第一个位置

具体的使用例子是:

func Combine() {
	bookList := MakeBook()
	query := linq.From(bookList)
	query1 := query.Concat(query) //不会排除重复项
	query2 := query.Union(query)  //会排除重复项
	fmt.Println(fmt.Sprintf("不会排除重复项的方法返回的元素个数是%d", len(query1.Results())))
	fmt.Println(fmt.Sprintf("会排除重复项的方法返回的元素个数是%d", len(query2.Results())))
	query1 = query1.Append(Book{Name: "Last One"})   //加在原来的Query的最后面
	query2 = query2.Prepend(Book{Name: "First One"}) //加在原来的Query的第一个
	fmt.Println(fmt.Sprintf("最后一个元素是%v", query1.Last()))
	fmt.Println(fmt.Sprintf("第一个元素是%v", query2.First()))
}

需要注意的是,query在调用了对应的方法之后,必须赋值给新的query(用等号),这样才能得到对应的结果.

 

7、Contains:判断元素是否在Query中

需要注意的是Go在判断元素equals的操作,这边需要放到Query中的应该是结构体数组,而不是指针数组,如果是指针数组则判断的是指针地址。如果是结构体数组,则根据Go的判断,所有元素值相等则该结构体相等

Contains的具体使用方法如下:

func Contains() {
	bookList := MakeBook()
	query := linq.From(bookList)
	//这边需要注意是的是如果要判断两个元素是否相等,在go里面不能使用元素的地址
	// 所以这边加到query里面的必须不能是元素的地址,否则没办法比较结构体本身,而是比较结构体的地址
	fmt.Println(fmt.Sprintf("判断当前数组是否包含相同的元素,判断的是所有的值,而不是地址,结果是%v", query.Contains(Book{
		Name:        "Go语言",
		Author:      "Go",
		Money:       100,
		WordsNum:    1000,
		PublishTime: time.Date(2020, 1, 1, 10, 0, 0, 0, time.Local),
	})))
}

 

8、First、Last:第一个最后一个

First和Last分别提供了三个方法:

                 First返回Query中数组的第一个元素

                 FirstWith、FirstWithT:返回满足条件的Query数组的第一个元素

                 Last返回Query中数组的最后一个元素

                 LastWith、LastWithT:返回满足条件的Query数组的最后一个元素

具体的使用例子如下:

func FirstAndLast() {
	bookList := MakeBook()
	query := linq.From(bookList)
	fmt.Println(fmt.Sprintf("数组第一本书书名叫:%s", query.First().(Book).Name))
	fmt.Println(fmt.Sprintf("数组第一本书发布时间在2020年1月1号之前的书书名叫:%s", query.FirstWith(PublishTimeBeforeFunc).(Book).Name))

	fmt.Println(fmt.Sprintf("数组最后一本书书名叫:%s", query.Last().(Book).Name))
	fmt.Println(fmt.Sprintf("数组最后一本书发布时间在2020年1月1号之前的书书名叫:%s", query.LastWith(PublishTimeBeforeFunc).(Book).Name))
}

9、Distinct:过滤数组使所有元素唯一

Distinct提供了三个方法

                  Distinct:判断结构体是否相等,返回所有唯一的结构体

                  DistinctBy、DistinctByT:根据对应的字段来判断是否相等,返回唯一的结构体.

使用例子如下:

func Distinct() {
	bookList := MakeBook()
	query := linq.From(bookList)
	query = query.Append(Book{
		Name:        "Go语言",
		Author:      "Go",
		Money:       100,
		WordsNum:    1000,
		PublishTime: time.Date(2020, 1, 1, 10, 0, 0, 0, time.Local),
	})
	bookList1 := make([]Book, 0)
	query.ToSlice(&bookList1)
	fmt.Println(fmt.Sprintf("筛选之前query中总共有%d个数据", query.Count()))
	//这边的distinct如果是结构体,则判断的是结构体的所有值,如果是指针则判断的是指针地址.
	query = query.Distinct()
	fmt.Println(fmt.Sprintf("筛选之后query中总共有%d个数据", query.Count()))

	fmt.Println()
	fmt.Println("输出对应书名和作者")
	query = query.Append(Book{
		Name:        "Go语言(第二版)",
		Author:      "Go",
		Money:       100,
		WordsNum:    1000,
		PublishTime: time.Date(2020, 1, 1, 10, 0, 0, 0, time.Local),
	})
	//根据对应的规则来判断是否相同 -- 如果对应的字段一样,只会保留第一个找到的,后面的会被清除掉
	query.DistinctByT(func(book Book) string {
		return book.Author
	}).ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("书名%v,作者%v", book.Name, book.Author))
	})
}

10、Except:求差集

Except提供两个方法:

                    Except:    根据结构体筛选两个Query之间的差集

                    ExceptBy:根据结构体的字段筛选两个Query之间的差集

实际这边是求得Query1中有但是Query2中没有的元素,具体的使用例子如下:

func Except() {
	bookList1 := MakeBook1()
	query1 := linq.From(bookList1)
	bookList2 := MakeBook2()
	query2 := linq.From(bookList2)

	fmt.Println("输出对应书名和作者")
	//判断结构结构体本身是否相等
	query1.Except(query2).
		ForEachT(func(book Book) {
			fmt.Println(fmt.Sprintf("书名%v,作者%v", book.Name, book.Author))
		})

	fmt.Println()
	fmt.Println("输出对应书名和作者")
	//判断结构体的某个字段是否出现
	query1.ExceptByT(query2, func(book Book) string {
		return book.Author
	}).ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("书名%v,作者%v", book.Name, book.Author))
	})
}

                 

11、GroupBy:分组

GroupBy提供两个方法,提供自定义结构体的GroupByT方法和GroupBy方法。

两个方法直接指定分组对应的key和Group.对于分组的元素,可以通过结构体的一个字段作为它的key,分组结果可以是结构体也可以是某个字段或者规则

具体的使用例子如下:

func GroupBy() {
	bookList1 := MakeBook1()
	query := linq.From(bookList1)

	fmt.Println("输出对应书名和作者")
	//第一个func是分组的依据,第二个func是分组后返回的元素.
	query = query.GroupByT(func(book Book) string {
		return book.Author
	}, func(book Book) Book {
		return book
	})

	query.ForEachT(func(bookGroup linq.Group) {
		fmt.Println(fmt.Sprintf("作者是%v", bookGroup.Key))
		for _, item := range bookGroup.Group {
			fmt.Println(fmt.Sprintf("书名%v,作者%v", item.(Book).Name, item.(Book).Author))
			fmt.Println()
		}

	})
	fmt.Println()
	fmt.Println("输出对应书名")

	bookList1 = MakeBook1()
	query = linq.From(bookList1)
	var nameGroups []linq.Group
	query.GroupByT(
		func(book Book) string { return book.Author },
		func(book Book) string { return book.Name },
	).ToSlice(&nameGroups)

	for _, item := range nameGroups {
		fmt.Println(fmt.Sprintf("作者是%v", item.Key))
		for _, row := range item.Group {
			fmt.Println(fmt.Sprintf("书名%v", row))
		}
	}
}

12、Intersect:求交集

Intersect提供了三个方法

                     Intersect:根据结构体判断进行求交集

                     IntersectBy、IntersectByT:根据结构体字段或者规则来判断交集

具体使用例子如下:

func Intersect() {
	bookList1 := MakeBook1()
	query1 := linq.From(bookList1)
	bookList2 := MakeBook2()
	query2 := linq.From(bookList2)

	fmt.Println("输出对应的书籍元素,按照结构体判断交集")
	query3 := query1.Intersect(query2)
	query3.ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("书名%v,作者%v", book.Name, book.Author))
	})

	fmt.Println()
	fmt.Println("输出对应的书籍元素,按照元素判断交集")
	query3 = query1.IntersectByT(query2, func(book Book) string {
		return book.Author
	})
	query3.ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("书名%v,作者%v", book.Name, book.Author))
	})

	//这边计算是按照结构体的一个元素进行比较。事实证明无论是否按照结构体的元素筛选,返回的都是完全相同的元素
	fmt.Println()
	fmt.Println("输出对应的书籍元素,按照元素判断交集2")
	bookList1 = MakeBook1()
	query1 = linq.From(bookList1)
	bookList3 := MakeBook3()
	query2 = linq.From(bookList3)
	query3 = query2.IntersectByT(query1, func(book Book) string {
		return book.Author
	})
	query3.ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("书名%v,作者%v", book.Name, book.Author))
	})

	fmt.Println()
	fmt.Println("判断是否会不重复的处理")
	query1 = linq.From([]int{1, 2, 3, 4, 5, 3})
	query2 = linq.From([]int{1, 3, 3, 78, 99, 0, 111})
	query3 = query2.IntersectByT(query1, func(i int) int {
		return i
	})
	fmt.Println(query3.Results())
}

13、OrderBy:排序

linq提供了多个排序方法,这边包括了二次排序的方法.

              按照从小到大的顺序排: Orderby 、OrderByT

              按照从大到小的顺序排:OrderByDescending、OrderByDescendingT

              在上一次的排序基础上按照从小到大的顺序排:ThenBy、ThenByT

              在上一次的排序基础上按照从大到小的顺序排:ThenByDescending、ThenByDescendingT

具体的使用例子如下:

//OrderBy可以按照对应的规则进行排序,可以实现多重排序,需要注意的就是OrderBy是从到达,orderByDescending是从大到小
func OrderBy() {
	bookList := MakeBook()
	query := linq.From(bookList)
	query = query.OrderByDescendingT(func(book Book) string {
		return book.Name
	}).ThenByDescendingT(func(book Book) string {
		return book.Author
	}).ThenByDescendingT(func(book Book) float64 {
		return book.Money
	}).Query

	query.ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("书名%v,作者%v", book.Name, book.Author))
	})

	fmt.Println()
	fmt.Println()

	//reverse: 把结果逆序
	query.Reverse().ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("书名%v,作者%v", book.Name, book.Author))
	})
}

14、Skip:略过满足指定条件的元素

Skip这边提供了三个方法:

              Skip(num):略过多少个元素,取剩下的元素

              SkipWhile(predicate)、SkipWhileT(predicate):忽略满足条件的对应数组元素,取剩下的元素

具体的使用例子如下:

func Skip() {
	bookList := MakeBook()
	query := linq.From(bookList)

	fmt.Println(fmt.Sprintf("跳过三个元素,返回的数组中包含%d个元素", len(query.Skip(3).Results())))
	fmt.Println(fmt.Sprintf("跳过发布时间在2018.1.1之后的元素,返回的数组中包含%d个元素", query.SkipWhile(PublishTimeAfterFunc).Count()))
}

15、Count:计算数组长度

Count提供了三个方法:

              Count:计算当前Query携带的数组长度

              CountWith、CountWithT:计算满足条件的当前Query携带的数组长度

具体的使用例子如下:

func Count() {
	bookList := MakeBook()
	query := linq.From(bookList)
	fmt.Println(fmt.Sprintf("数组中发布时间早于2019-1-1的书有%d本", query.CountWithT(PublishTimeBeforeFunc)))
	fmt.Println(fmt.Sprintf("数组中发布时间晚于2018-1-1的书有%d本", query.CountWithT(PublishTimeAfterFunc)))
}

 

16、ToMap:转换为Map

ToMap提供了三个方法

           ToMap:     需要配合 SelectT 方法去生成返回一个 linq.KeyValue的结构,指定对应的Map的key和value

           ToMapBy、ToMapByT:提供两个方法,一个方法指定key,一个方法指定value

如果key相同的话,前面的元素会被覆盖掉,ToMap方法的使用需要配合Select方法.

具体的使用例子如下:

func ToMap() {
	bookList := MakeBook()
	query := linq.From(bookList)

	bookMap := make(map[string]string, 0)
	query.SelectT(func(book Book) linq.KeyValue {
		return linq.KeyValue{
			Key:   book.Author,
			Value: book.Name,
		}
	}).ToMap(&bookMap)

	for key, value := range bookMap {
		fmt.Println(fmt.Sprintf("作者%v,写了一本书叫做%v", key, value))
	}

	fmt.Println()
	fmt.Println()

	//如果key相同的话,前面的元素会被覆盖掉(只会保留最后一个)
	bookList1 := MakeBook1()
	query1 := linq.From(bookList1)
	bookMap1 := make(map[string]string, 0)
	query1.SelectT(func(book Book) linq.KeyValue {
		return linq.KeyValue{
			Key:   book.Author,
			Value: book.Name,
		}
	}).ToMap(&bookMap1)

	for key, value := range bookMap1 {
		fmt.Println(fmt.Sprintf("作者%v,写了一本书叫做%v", key, value))
	}

	fmt.Println()
	fmt.Println()
	bookList2 := MakeBook()
	query2 := linq.From(bookList2)

	bookMap2 := make(map[string]Book, 0)
	query2.ToMapByT(&bookMap2,
		//这个方法决定key的字段是什么
		func(book Book) string {
			return book.Author
		}, //这个方法决定value的字段是什么
		func(book Book) Book {
			return book
		})

	for key, value := range bookMap2 {
		fmt.Println(fmt.Sprintf("作者%v,写了一本书叫做%v", key, value.Name))
	}
}

17、ToSlice:转换为数组

ToSlice提供了转换数组的方法,具体例子如下:

func ToSlice(){
	fmt.Println()
	fmt.Println()
	bookListResult := make([]Book,0)
	linq.From(MakeBook()).OrderByDescendingT(func(book Book) string {
		return book.Author
	}).ToSlice(&bookListResult)
	for _, value := range bookListResult {
		fmt.Println(fmt.Sprintf("作者%v,写了一本书叫做%v", value.Author, value.Name))
	}
}

18、Take:提取对应数量的元素

Take方法提供了三个方法:

             Take(num):提取对应数量的元素

             TakeWhile、TakeWhileT(predicate):提取满足条件的元素,一旦某一个元素不满足条件,直接返回,不会再判断后面的元素

TakeWhile一旦遇到不满足条件的元素直接返回,不会再往下查找,一定要注意这个,不然在使用的时候很可能犯错.

具体的使用例子如下:


func Take() {
	bookList := MakeBook()
	query := linq.From(bookList)
	query.OrderByDescendingT(func(book Book) string {
		return book.Author
	}).Take(3).ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("作者%v,写了一本书叫做%v", book.Author, book.Name))
	})

	fmt.Println()
	fmt.Println()

	//超出对应数量的元素会不会报错(输出所有的元素)
	query.OrderByDescendingT(func(book Book) string {
		return book.Author
	}).Take(100).ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("作者%v,写了一本书叫做%v", book.Author, book.Name))
	})

	fmt.Println()
	fmt.Println()
	//负数会发生什么情况(直接返回,不做操作)
	query.OrderByDescendingT(func(book Book) string {
		return book.Author
	}).Take(-2).ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("作者%v,写了一本书叫做%v", book.Author, book.Name))
	})

	//TakeWhile系列的方法,虽然能够根据条件进行查询,但是有一个弊端就是一旦查询到的结果和条件不符合则直接返回,也就是说后面满足条件的元素不会再被做筛选
	//那么比如这边的这个大于的操作,就需要对整个数组进行排序之后然后进行筛选,如果不希望排序,则用原生的for循环可能会更好
	fmt.Println()
	fmt.Println()
	query.TakeWhileT(func(book Book) bool {
		return book.Money > float64(70)
	}).ForEachT(func(book Book) {
		fmt.Println(fmt.Sprintf("作者%v,写了一本书叫做%v", book.Author, book.Name))
	})
}

19、Select:筛选结构体字段/规则操作

Select提供四个方法,主要用来对结构体字段进行处理筛选,分别如下:

              Select、SelectT:筛选对应的字段。

              SelectIndexd、SelectIndexdT:筛选对应字段,这边可以拿到对应结构体的下标信息。

具体的使用例子如下:

func Select() {
	bookList := MakeBook()
	query := linq.From(bookList)

	//筛选出结构体对应的元素
	query.SelectT(func(book Book) string {
		return book.Name
	}).ForEachT(func(bookName string) {
		fmt.Println(fmt.Sprintf("书名%v", bookName))
	})

	fmt.Println()
	fmt.Println()

	//根据下标进行返回,如果是跟下标有关的,则可以通过这个方法进行组合然后返回.
	query.SelectIndexedT(func(index int, book Book) []string {
		return []string{strconv.Itoa(index + 1), book.Name}
	}).ForEachT(func(book []string) {
		fmt.Println(fmt.Sprintf("第%v本书,书名%v", book[0], book[1]))
	})
}

20、SelectMany:扁平化多维数组

SelectMany主要用于多维数组或者是嵌套结构体的处理,这边提供了四个方法:

              SelectMany、SelectManyT:对多维度数组进行处理,传入一个返回 linq.Query对象的方法,一次之后把二维数组扁平化为一维数组

              SelectManyBy、SelectManyByT:将集合的每个元素投影到一个Query,将结果集合迭代和展平为一个集合,并在其中的每个元素上调用结果,比较厉害的一个就是拿到结构体数组中的对应子结构体,然后通过第二个方法拿到子结构和子结构体对应的结构体进行处理,具体可以看下面的代码进行理解:

func SelectMany() {
	input := [][]int{{1, 2, 3}, {4, 5, 6, 7}}

	//二位数组进行合并
	linq.From(input).SelectManyT(
		func(i []int) linq.Query {
			return linq.From(i)
		},
	).ForEachT(func(num int) {
		fmt.Println(num)
	})

	fmt.Println()
	fmt.Println()

	//三维数组进行合并
	input1 := [][][]int{{{1, 2, 3}}, {{4, 5, 6, 7}, {8, 9, 10}}}
	linq.From(input1).SelectManyT(
		func(i [][]int) linq.Query {
			return linq.From(i)
		},
	).SelectManyT(
		func(i []int) linq.Query {
			return linq.From(i)
		},
	).ForEachT(func(num int) {
		fmt.Print(num)
		fmt.Print(" ")
	})

	//SelectMany的主要作用是,结构体中包含数组元素,可以把数组元素取出来和结构体做相应的操作
	//如果是结构体中有多个数组的操作,可以把对应的多个数组放到一个新定义的结构体数组中
	fmt.Println()
	men := MakeInnerData()
	linq.From(men).
		//第一个方法筛选出结构体对应的数组
		SelectManyByT(func(man Man) linq.Query {
			return linq.From(man.Pets)
		},
			//第二个方法这边可以拿到数组中元素对应的结构体的数据.
			func(pet Pets, man Man) map[string]Pets {
				return map[string]Pets{
					man.Name: pet,
				}
			}).ForEachT(func(petWithMan map[string]Pets) {
		for key, value := range petWithMan {
			fmt.Println(fmt.Sprintf("%v拥有一只宠物,它的名字叫做%v", key, value.Name))
		}
	})

}

21、Join:关联结构体

Join是把两个Query根据某个字段或者某种规则进行连接的操作,这边提供了两个方法

          Join、JoinT:第一个入参是Query1的元素的连接字段或者规则,第二个参数是Query2对应的连接字段或者规则,当两个规则对上的时候就可以进行连接.第三个参数是 func(Query1对应的元素,Query2对应的元素),因为这边可能存在没有连接对象的情况,所以要注意处理nil的情况,第三个方法对两个对应上的元素进行处理。

具体代码例子如下:

func Join() {
	//任务和动物有关联,直接找到对应的关系,没有对应关系的数据不会处理,正常情况下应该用更单一的数据来做关联
	persons, pets := MakeJoinData()
	query1 := linq.From(persons)
	query2 := linq.From(pets)
	query1.JoinT(query2,
		func(person Person) string {
			return person.Name
		},
		func(pet Pet) string {
			return pet.OwnerName
		},
		func(person Person, pet Pet) map[string]Pet {
			petMap := make(map[string]Pet)
			petMap[person.Name] = pet
			return petMap
		},
	).ForEachT(func(petMap map[string]Pet) {
		for key, value := range petMap {
			fmt.Println(fmt.Sprintf("%v拥有一只宠物,它的名字叫做:%v", key, value.Name))
		}
	})
}

 

Ⅲ.注意事项

  1. 使用不指定类型的方法要比指定类型的方法来的更高效,比如说Select比SelectT来的更高效,因为SelectT还需要根据对应的类型去生成对应的入参方法,而Select则不需要,所以来的更高效
  2. ToMap、ToSlice、To...的方法,最后生成的结构体,该方法的入参必须是结构体指针,否则没有办法生成对应的属性
  3. TakeWhile方法和Where有点类似,但是需要注意的是Where会返回所有满足条件的元素,而TakeWhile一旦遇到不满足的元素就直接返回了,不会再过滤后面的元素
  4. 使用linq方法需要考虑数组为nil,对应分组或者join的元素为nil的情况

 

 类似资料: