Go集合流式API - GoStream

TL;DR

GoStream是个参考Java StreamAPI,基于go-linq实现的数据处理库。
它可以声明式地对数据进行转换、过滤、排序、分组、收集,而无需关心操作细节。

背景

项目中经常需要对数据进行分组、转换,已有的数据处理库go-linq、go-streams都无法满足我需求。API不友好、不支持多级分组… 所以想新写个Libary,于是,有了GoStream!!

示例

更完整的例子,请参考walkthrough.go

Base Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
input := []int{4, 3, 2, 1}
want := []int{6, 8}

got := From(input).Map(func(it interface{}) interface{} {
return 2 * it.(int)
}).Filter(func(it interface{}) bool {
return it.(int) > 5
}).SortedBy(func(it interface{}) interface{} {
return it
}).Collect(ToSlice([]int(nil)))

if !reflect.DeepEqual(got, want) {
panic(fmt.Sprintf("%v != %v", got, want))
}

Map & FlatMap

Map和FlatMap的差别在于:FlatMap的mapper返回一个Stream,用于将多级数据打扁。

1
2
3
4
5
6
7
input := [][]int{{3, 2, 1}, {6, 5, 4}, {9, 8, 7}}
want := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
got := From(input).FlatMap(func(it interface{}) Stream {
return From(it)
}).SortedBy(func(it interface{}) interface{} {
return it
}).Collect(ToSlice([]int{}))

Collect ToSlice & ToMap

Collect将数据收集起来。受限于go的类型系统,需要显示传类型参数——一个目标类型的实例,可以为nil。

1
2
3
4
5
6
7
intput := []int{1, 2, 3, 4, 5}
identity := func(it interface{}) interface{} { return it }

// []int{1, 2, 3, 4, 5}
gotSlice := From(intput).Collect(ToSlice([]int{}))
// map[int]int{1: 1, 2: 2, 3: 3, 4: 4, 5: 5}
gotMap := From(intput).Collect(ToMap(map[int]int(nil), identity, identity))

Collect GroupBy

GroupBy定义一个分组收集器,参数依序分别为 类型参数、分类方法、下游收集器。
GroupBy可以和ToSlice、ToMap组合,GroupBy也可以多级嵌套,实现多级分组。
GroupBy(typ interface{}, classifier normalFn, downstream collector) collector

假设一组货物,需要按Status,Location进行分组,目标类型为 map[int]map[string][]*Cargo。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// Cargo 货物实体
type Cargo struct {
ID int
Name string
Location string
Status int
}

input := []*Cargo{{
ID: 1,
Name: "foo",
Location: "shenzhen",
Status: 1,
}, {
ID: 2,
Name: "bar",
Location: "shenzhen",
Status: 0,
}, {
ID: 3,
Name: "zhang",
Location: "guangzhou",
Status: 1,
}}

getStatus := func(it interface{}) interface{} { return it.(*Cargo).Status }
getLocation := func(it interface{}) interface{} { return it.(*Cargo).Location }

// result type: map[int]map[string][]*Cargo
got := From(input).Collect(
GroupBy(map[int]map[string][]*Cargo(nil), getStatus,
GroupBy(map[string][]*Cargo(nil), getLocation,
ToSlice([]*Cargo(nil)))))

Flatten Group

这个示例演示如何将多级分组Map扁平化。map[int]map[string][]*Cargo => []*Cargo

1
2
3
4
5
From(cargoByLocationByTo).FlatMap(func(it interface{}) Stream {
return From(it.(KeyValue).Value).FlatMap(func(it2 interface{}) Stream {
return From(it2.(KeyValue).Value)
})
}).Collect(ToSlice([]*Cargo{}))

Go集合流式API - GoStream
https://a3d21.github.io/2021/11/16/2021-11-16-gostream/
作者
a3d21
发布于
2021年11月16日
许可协议