点击上方蓝色“飞雪无情”关注我的公众号,设个星标,第一时间看文章
在 Go 语言中,slice、map都是我们常用的基础类型,通过它们,我们可以很容易的使用数据。但是你有没有发现,为了对这两种数据进行处理,你不得不编写很多工具函数?
比如,从slice切片中查找一个元素的位置?这种查找又分为从前查找、从后查找。
又比如,获取map的所有keys?或者所有的value?
再比如,JS语言数组的map、reduce、filter函数,这在编程中非常好用,但是遗憾的是Go标准库没有提供。
这些例子,还有很多,而且都是我们编程中常用的工具函数,但是我们的 Go SDK 却没有。
但是我们又需要怎么办?一种办法,是我们自己编写一个工具包,用于给项目使用,但是这个工具包的维护,又是一个问题,需要人力。
第二种方式,就是用开源的库,经过充分的测试、验证,并且经常更新,所以可以保障。
因为我们遇到的是常见的问题,所以有人做了开源库,以供大家使用。这种工具库以 Go 1.18为分界线,Go 1.18之前有一款比较出名的是 go-funk。
很有意思的名字,Repo 地址是 https://github.com/thoas/go-funk。官方介绍就是一个Go语言工具库:
A modern Go utility library which provides helpers (map, find, contains, filter, ...)
它提供了很多好用的函数:Contains、Difference、IndexOf、LastIndexOf等等,具体更多的可以参考它的网站。
但是它有一个致命的问题,就是用到了反射。这也是没办法的事情,因为在Go 泛型没有支持之前,只能通过反射才能写出满足不同类型的函数。
举个IndexOf的例子,假如不用反射,要想支持更多的类型,就得定义很多相似名称的函数,如下所示。
func IndexOfBool(a []bool, x bool) int {
}
func IndexOfInt(a []int, x int) int {
}
func IndexOfInt32(a []int32, x int32) int {
}
func IndexOfInt64(a []int64, x int64) int {
}
func IndexOfUInt(a []uint, x uint) int {
}
func IndexOfUInt32(a []uint32, x uint32) int {
}
func IndexOfUInt64(a []uint64, x uint64) int {
}
func IndexOfFloat64(a []float64, x float64) int {
}
func IndexOfString(a []string, x string) int {
}
以上函数行不行?当然行,但是会写很多重复的代码,并且看着也怪怪的。
在Go语言的泛型支持之前,要解决这个问题,只能通过反射。
// IndexOf gets the index at which the first occurrence of value is found in array or return -1
// if the value cannot be found
func IndexOf(in interface{}, elem interface{}) int {
inValue := reflect.ValueOf(in)
elemValue := reflect.ValueOf(elem)
inType := inValue.Type()
if inType.Kind() == reflect.String {
return strings.Index(inValue.String(), elemValue.String())
}
if inType.Kind() == reflect.Slice {
equalTo := equal(elem)
for i := 0; i < inValue.Len(); i++ {
if equalTo(reflect.Value{}, inValue.Index(i)) {
return i
}
}
}
return -1
}
泛型代码复杂,并且效率低。。。
那么Go 1.18之后已经支持了泛型,能不能用泛型来重写呢?
答案是:当然可以,并且已经有人这么做了。这个库就是 https://github.com/samber/lo ,也是我们今天要介绍的主角,开源3个月,6000多star!!!
它是基于Go泛型实现,没有用到反射,效率高,代码简洁。比如刚刚的IndexOf函数,在该库中是这么实现的:
// IndexOf returns the index at which the first occurrence of a value is found in an array or return -1
// if the value cannot be found.
func IndexOf[T comparable](collection []T, element T) int {
for i, item := range collection {
if item == element {
return i
}
}
return -1
}
只需要 T 被约束为comparable 的,就可以使用==符号进行比较了,整体代码非常简单,并且没有反射。
IndexOf只是lo几十个函数中的一个,这些函数基本上覆盖了slice、map、string等方方面面,涉及查找、比较大小、生成、map、reduce、过滤、填充、反转、分组等等,使用方法和示例,可以参考go doc文档。https://pkg.go.dev/github.com/samber/lo
Supported helpers for slices:
Filter
Map
FilterMap
FlatMap
Reduce
ForEach
Times
Uniq
UniqBy
GroupBy
Chunk
PartitionBy
Flatten
Shuffle
Reverse
Fill
Repeat
KeyBy
Drop
DropRight
DropWhile
DropRightWhile
Reject
Count
CountBy
Supported helpers for maps:
Keys
Values
PickBy
PickByKeys
PickByValues
OmitBy
OmitByKeys
OmitByValues
Entries
FromEntries
Invert
Assign (merge of maps)
MapKeys
MapValues
Supported math helpers:
Range / RangeFrom / RangeWithSteps
Clamp
SumBy
Supported helpers for strings:
Substring
RuneLength
Supported helpers for tuples:
T2 -> T9
Unpack2 -> Unpack9
Zip2 -> Zip9
Unzip2 -> Unzip9
Supported intersection helpers:
Contains
ContainsBy
Every
EveryBy
Some
SomeBy
None
NoneBy
Intersect
Difference
Union
Supported search helpers:
IndexOf
LastIndexOf
Find
FindIndexOf
FindLastIndexOf
Min
MinBy
Max
MaxBy
Last
Nth
Sample
Samples
Conditional helpers:
Ternary (1 line if/else statement)
If / ElseIf / Else
Switch / Case / Default
Type manipulation helpers:
ToPtr
ToSlicePtr
ToAnySlice
FromAnySlice
Empty
Coalesce
Concurrency helpers:
Attempt
AttemptWithDelay
Debounce
Async
Error handling:
Must
Try
TryCatch
TryWithErrorValue
TryCatchWithErrorValue
Constraints:
Clonable
随着Go语言泛型的完善,我相信这些工具函数,会成为Go标准库的一部分,被更多开发者使用。
现在,先把lo用起来吧~~~~
Go 团队在 GopherCon 2021 上的演讲:Go 1.18 泛型简介
一个简单的Golang实现Socket5 Proxy
Go 1.18 的工作区模式
扫码关注
分享、点赞、在看就是最大的支持