在业务开发过程中除了业务逻辑、数据库之外主要就是数据转换处理过滤等相关的内容,比如合法性验证(可以使用”go-playground/validator“)但是golang在数据处理上面尤其是对结构体上官方几乎没有提供一些便捷的方式,大部分都只能使用for、if等方式自己来处理,也有研发用偷懒的方式全部交给数据库写出了N张表的关联导致了数据库瓶颈等问题。
那么有没有什么更加方便好用的方法来更加方便的处理数据又能够降低代码复杂度,写出更加可读的代码呢?答案是肯定了,比如go-zero中的fx流处理比较适合离线数据统计处理和mr比较适合实时数据并发处理,但今天给大家介绍的主角是“go-linq”通过类ORM的链式方式来进行数据处理。
资料:
golint特性:
首先我们来看一个例子,一个简单的struct获取age大于等于18的ID变成一个slice来给到后面的SQL执行in查询,按照一般的做法就直接开始for循环+if判断+赋值了。
type Person struct {
ID int64
Age int64
Name string
}
func getPersonIDList(persons []*Person) []int64 {
personIDList := make([]int64, 0, len(persons))
for _, person := range persons {
if person.Age >= 18 {
personIDList = append(personIDList, person.ID)
}
}
return personIDList
}
如果是java程序是怎么做的呢? 基于java8的特性可以很方便的通过stream -> filter -> map -> collect来获取出想要的内容也非常简洁
public class Test (
@Data
public static class Person (
private Long id;
private Integer age;
private String name;
}
public List<Long> getPersonIdList(List<Person> personList) {
return personList.stream()
.filter(person -> person.age >= 18)
.map(Person::getId).collect(Collectors. toList());
}
}
导入依赖包
go get github.com/ahmetb/go-linq/v3
如果使用了go-linq也就可以实现类似的效果了,看起来清爽很多
type Person struct {
ID int64
Age int64
Name string
}
func getPersonIDList(persons []*Person) (personIDList []int64) {
linq.From(persons).
WhereT(func(person *Person) bool { return person.Age >= 18 }).
SelectT(func(person *Person) int64 { return person.ID }).ToSlice(&personIDList)
return
}
PS:如果你在意性能并且list比较大就不推荐使用WhereT、SelectT,应该使用Where、Select通过interface来接受入参进行断言避免反射带来的开销,纯性能差距大改造 5 到 10 倍
官方例子:查找出书最多的作者
type Book struct {
id int
title string
authors []string
}
var books []Book
author := From(books).
SelectMany( // make a flat array of authors
func(book interface{}) Query {
return From(book.(Book).authors)
}).
GroupBy( // group by author
func(author interface{}) interface{} {
return author // author as key
}, func(author interface{}) interface{} {
return author // author as value
}).
OrderByDescending( // sort groups by its length
func(group interface{}) interface{} {
return len(group.(Group).Group)
}).
Select( // get authors out of groups
func(group interface{}) interface{} {
return group.(Group).Key
}).
First() // take the first author
fruits := []string{"orange", "apple", "lemon", "apple"}
linq.From(fruits).ForEachIndexedT(func(i int, fruit string) {
fmt.Println(i, fruit)
})
fruits := []string{"apple",
"passionfruit",
"banana",
"mango",
"orange",
"blueberry",
"grape",
"strawberry"}
var query []string
linq.From(fruits).WhereT(func(f string) bool { return len(f) > 6 }).ToSlice(&query)
fmt.Println(query)
numbers := []int{0, 30, 20, 15, 90, 85, 40, 75}
var query2 []int
linq.From(numbers).WhereIndexedT(func(index int, number int) bool { return number <= index*10 }).ToSlice(&query2)
fmt.Println(query2)
}
type Product struct {
Name string
Code int
}
func Test_Distinct(t *testing.T) {
ages := []int{21, 46, 46, 55, 17, 22, 55, 55}
var distinctAges []int
linq.From(ages).
OrderBy(
func(item interface{}) interface{} { return item },
).
Distinct().
ToSlice(&distinctAges)
fmt.Println(distinctAges)
products := []Product{
{Name: "apple", Code: 9},
{Name: "orange", Code: 4},
{Name: "apple", Code: 9},
{Name: "Lemon", Code: 12},
}
var noduplicates []Product
linq.From(products).
DistinctByT(
func(item Product) int { return item.Code },
).
ToSlice(&noduplicates)
for _, product := range noduplicates {
fmt.Printf("%s %d\n", product.Name, product.Code)
}
fruits1 := []Product{
{Name: "apple", Code: 9},
{Name: "orange", Code: 4},
{Name: "apple", Code: 9},
{Name: "Lemon", Code: 12},
}
fruits2 := []Product{{Name: "apple", Code: 9}}
//Order and exclude duplicates.
var except []Product
linq.From(fruits1).
ExceptByT(linq.From(fruits2),
func(item Product) int { return item.Code },
).
ToSlice(&except)
for _, product := range except {
fmt.Printf("%s %d\n", product.Name, product.Code)
}
storel := []Product{
{Name: "orange", Code: 4},
{Name: "apple", Code: 9},
}
store2 := []Product{
{Name: "lemon", Code: 12},
{Name: "apple", Code: 9},
}
var duplicates []Product
linq.From(storel).
IntersectByT(linq.From(store2),
func(p Product) int { return p.Code },
).
ToSlice(&duplicates)
for _, p := range duplicates {
fmt.Println(p.Name, p.Code)
}
更多的例子可以参考官方的单测实现: