抱歉,您的瀏覽器無法訪問本站
本頁面需要瀏覽器支持(啟用)JavaScript
了解詳情 >

前言

大伙都知道在今年的3月份,Go官方在1.18版本中推出了泛型的特性。本文就是介绍一个基于1.18版本的Go工具类库samber/lo,该库相对于其他使用反射来实现的库来说,更加的快、同时还安全。

它提供了切片的许多辅助函数。例如:FilterSliceFillMapFilterMapFlatMapGroupByPartitionBy等,还提供了类似Java中的try-catch机制的异常处理函数,例如:TryTryWithErrorValueTryCatch

install

go get github.com/samber/lo@v1

import

1
2
3
4
import (
"github.com/samber/lo"
lop "github.com/samber/lo/parallel"
)

通常我们只导入github.com/samber/lo就足够了,lop是一个使用协程并发执行的模块,里面只包含了MapForEachTimesGroupByPartitionBy这五个并行函数。

使用

Slice的辅助函数们

Filter
1
2
3
4
5
6
func testFilter() {
strs := lo.Filter[string]([]string{"hello", "good bye", "world", "fuck", "fuck who"}, func(s string, _ int) bool {
return !strings.Contains(s, "fuck")
})
fmt.Println(strs) //[hello good bye world]
}

Filter函数的第一个参数就是元素类型切片[]V,第二个就是过滤函数func(V, int) bool,其中V就是所定义泛型类型,int为切片下标。

Map
1
2
3
4
5
6
7
func testMap() {
intNums := lo.Map[string, int]([]string{"1", "2", "3"}, func(s string, _ int) int {
num, _ := strconv.Atoi(s)
return num
})
fmt.Println(intNums) // [1 2 3]
}

Map函数用于将一种切片类型转换为另一种类型,例如上面的例子就是将string切片转换为int切片。

FlatMap
1
2
3
4
5
6
7
8
9
10
func testFlatMap() {
flatMap := lo.FlatMap[int64, string]([]int64{1, 8, 16, 20}, func(i int64, _ int) []string {
return []string{
strconv.FormatInt(i, 8),
strconv.FormatInt(i, 10),
strconv.FormatInt(i, 16),
}
})
fmt.Println(flatMap) // [1 1 1 10 8 8 20 16 10 24 20 14]
}

FlatMap函数将一种切片类型转换为另一种切片类型同时将其扁平化

GroupBy
1
2
3
4
5
6
7
func testGroupBy() {
group := lo.GroupBy[int, int]([]int{1, 2, 3, 4, 5, 6, 7, 8}, func(i int) int {
return i % 3
})
fmt.Println(group) // map[0:[3 6] 1:[1 4 7] 2:[2 5 8]]

}

GroupBy函数将切片类型按照分组函数分组,返回一个map类型的数据。

PartitionBy
1
2
3
4
5
6
7
8
9
10
11
12
13
func testPartitionBy() {
partitionBy := lo.PartitionBy[int, string]([]int{-3, -2, -1, 0, 1, 2, 3}, func(i int) string {
if i < 0 {
return "negative"
} else if i > 0 {
return "positive"
} else {
return "zero"
}
})
fmt.Println(partitionBy) // [[-3 -2 -1] [0] [1 2 3]]

}

PartitionBy将一个切片分组成二维切片,类似于GroupBy函数只不过一个返回是切片、一个是map类型

Count
1
2
3
4
func testCount() {
count := lo.Count([]int{1, 4, 7, 5, 3, 1}, 1)
fmt.Println(count) // 2
}

Count函数计算切片中等于第二个参数的元素的数量。

CountBy
1
2
3
4
5
6
func testCountBy() {
count := lo.CountBy([]int{1, 4, 7, 5, 3, 1}, func(i int) bool {
return i > 5
})
fmt.Println(count) // 1
}

CountBy函数计算比较函数为true的元素的数量

Map的辅助函数

Keys
1
2
3
4
5
func testKeys() {
keys := lo.Keys[string, int](map[string]int{"hello": 1, "world": 2})
fmt.Println(keys) // [hello world]

}

Keys函数返回map的key数组

Values
1
2
3
4
5
func testValues() {
values := lo.Values[string, int](map[string]int{"hello": 1, "world": 2})
fmt.Println(values) // [1 2]

}

Values函数返回map的value数组

异常处理的辅助函数

Must
1
2
3
4
5
6
7
func testMust() {
num := lo.Must(strconv.Atoi("1"))
fmt.Println(num) //1
num = lo.Must(strconv.Atoi("Hello World"))
fmt.Println(num) // panic: strconv.Atoi: parsing "Hello World": invalid syntax

}

Must函数通常用来包裹一个函数调用,如果它的第二个参数值为error或者false的话则会抛出panic,否则返回第一个参数值。

下面是Must函数的源码,可以看到就只是判断err参数是否为bool类型的false值或者为error类型,如果是则抛出panic。

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
34
func Must[T any](val T, err any, messageArgs ...interface{}) T {
must(err, messageArgs...)
return val
}

// must panics if err is error or false.
func must(err any, messageArgs ...interface{}) {
if err == nil {
return
}

switch e := err.(type) {
case bool:
if !e {
message := messageFromMsgAndArgs(messageArgs...)
if message == "" {
message = "not ok"
}

panic(message)
}

case error:
message := messageFromMsgAndArgs(messageArgs...)
if message != "" {
panic(message + ": " + e.Error())
} else {
panic(e.Error())
}

default:
panic("must: invalid err type '" + reflect.TypeOf(err).Name() + "', should either be a bool or an error")
}
}

Must函数还有不同参数的变种函数,不过行为是一致的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func example0() (error)
func example1() (int, error)
func example2() (int, string, error)
func example3() (int, string, time.Date, error)
func example4() (int, string, time.Date, bool, error)
func example5() (int, string, time.Date, bool, float64, error)
func example6() (int, string, time.Date, bool, float64, byte, error)

lo.Must0(example0())
val1 := lo.Must1(example1()) // alias to Must
val1, val2 := lo.Must2(example2())
val1, val2, val3 := lo.Must3(example3())
val1, val2, val3, val4 := lo.Must4(example4())
val1, val2, val3, val4, val5 := lo.Must5(example5())
val1, val2, val3, val4, val5, val6 := lo.Must6(example6())
Try
1
2
3
4
5
6
7
8
9
10
11
12
13
14
func testTry() {
ok := lo.Try(func() error {
fmt.Println("尝试工作")
panic("下班啦")
})
fmt.Println(ok) // false

ok = lo.Try(func() error {
fmt.Println("尝试工作")
fmt.Println("结束工作")
return nil
})
fmt.Println(ok) // true
}

Try函数当返回值不为nil或者函数抛出panic时则会返回false,否则返回true

TryWithErrorValue
1
2
3
4
5
6
7
8
9
10
func testTryWithErrorValue() {
err, ok := lo.TryWithErrorValue(func() error {
fmt.Println("尝试工作")
panic("下班啦")
})
fmt.Println(err, ok)
// 尝试工作
// 下班啦 false

}

TryWithErrorValue函数于Try函数逻辑是一样的,只不过多个painc的返回值

1
2
3
4
5
6
7
8
9
func testTryCatch() {
flag := false
lo.TryCatch(func() error {
panic(flag)
}, func() {
flag = true
})
fmt.Println(flag) // true
}

同Try函数相同的逻辑,只不过当出现panic时会调用第二个函数参数

小结

本文只是列举了一些samber/lo框架中常用的函数,samber/lo还提供了很多的辅助函数,例如对字符串、元组、搜索、条件判断都提供了相关的函数。同时通过上面列举的一些例子就可以发现该工具类框架确实可以减轻开发时的一些负担,让我们在crud时更顺手,如果还有还要了解更多的工具类函数就去官网看看吧~

官网链接