go:类似LINQ的库

398 阅读29分钟

在Go语言中,没有像C#中的LINQ (Language-Integrated Query) 那样的直接集成查询语言功能。不过,Go语言提供了一些标准库中的函数和方法,可以实现类似于LINQ的查询和操作。

ahmetb/go-linq 在go1.18之前用这个

samber/lo 在go1.18之后用这个,star更高

切片操作

名称函数描述
lo.Uniq切片去重,返回
lo.Filter过滤元素
lo.Maplop.Map映射
lo.FilterMap映射满足条件的元素
lo.FlatMap将切片转换为另外一种类型的切片(元素数可能更多)
lo.Reduce将集合结果返回单个值
lo.ReduceRight将集合结果返回单个值,从右向左计算
lo.ForEach对每个元素执行函数
lop.ForEach对每个元素执行函数,由协程实现,遍历元素不按照顺序
lo.Times执行n次函数,参数是执行次数
lop.Times执行n次函数,参数是执行次数
lo.UniqBy去重,去重函数可以自己定义
lo.GroupBylop.GroupByGroupBy返回一个map字典,该对象由通过iteratee运行集合的每个元素的结果生成的键组成。
lo.Chunk返回一个数组,其中的元素按大小分成若干组。如果数组不能平均分割,则最后的块将是剩余的元素
lo.PartitionBylo.PartitionBy()分组,自定义分组函数
lo.Flatten返回一层深度的切片
lo.Interleave循环交替输入切片,并依次将索引处的值附加到结果中。
lo.Shuffle打乱切片元素顺序
lo.Reverse反转元素顺序
lo.Fill用指定值填充切片,元素必须是Clonable
lo.Repeat复制元素n份,元素必须是Clonable
lo.RepeatBy用指定函数生成n份复制值,函数参数是次数
lo.KeyBy将切片转换为map,key由指定规则生成,值为元素值
lo.Associate将切片转换为map,key value都由指定方式生成
lo.Drop从切片的开头删除几个元素
lo.DropRight从切片的结尾删除几个元素
lo.DropWhile从开头删除元素,直到不满足指定条件
lo.DropRightWhile从结尾删除元素,直到不满足指定条件
lo.Reject返回不满足条件的元素切片
lo.Count返回序列中与指定元素值相等的元素个数
lo.CountBy返回序列中满足条件的元素个数
lo.CountValues统计序列中,每个元素的个数,结果为map
lo.CountValuesBy根据指定函数的返回值,来计数个数,结果为map
lo.Subset返回切片,指定起始位置和元素个数。溢出不会抛异常。起始位置<0,代表从右往左数(-1开始),对应的正的起始位置=元素个数+起始位置(为负数时)
lo.Slice返回切片,指定起始、终止下标,包括起始不包括终止。溢出不会抛异常。
lo.Replace替换切片中的元素,指定替换的个数,个数<0时,全部替换
lo.ReplaceAll全部替换切片中指定的元素
lo.Compact返回切片中的非零值
lo.IsSorted检查切片中的元素是否被排序
lo.IsSortedByKey检查切片中的元素是否被按照指定规则排序

去重、重复元素

Uniq UniqBy

FindUniques:返回唯一元素的切片,顺序与原顺序相同

FindUniquesBy:返回执行指定函数后返回值不相同的元素的切片

FindDuplicates:返回重复元素的切片

FindDuplicatesBy:返回执行指定函数后返回返回值相同的元素的切片

//names := lo.Uniq[string]([]string{"Samuel", "John", "Samuel"})
//编译器可以进行类型推断
names := lo.Uniq([]string{"Samuel", "John", "Samuel"})
fmt.Println(names)
// []string{"Samuel", "John"}

uniqValues := lo.UniqBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
    return i % 3
})
fmt.Println(uniqValues)
// []int{0, 1, 2}

uniqueValues := lo.FindUniques([]int{1, 2, 2, 1, 2, 3})
// []int{3}

uniqueValues := lo.FindUniquesBy([]int{3, 4, 5, 6, 7}, func(i int) int {
    return i%3
})
// []int{5}

duplicatedValues := lo.FindDuplicates([]int{1, 2, 2, 1, 2, 3})
// []int{1, 2}

duplicatedValues := lo.FindDuplicatesBy([]int{3, 4, 5, 6, 7}, func(i int) int {
    return i%3
})
// []int{3, 4}

过滤

even := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {
    return x%2 == 0
})
fmt.Println(even)
// []int{2, 4}

映射

直接映射Map

mapData := lo.Map([]int64{1, 2, 3, 4}, func(x int64, index int) string {
    return strconv.FormatInt(x, 10)
})
fmt.Println(mapData)
// []string{"1", "2", "3", "4"}

//lop包
lopMapData := lop.Map([]int64{1, 2, 3, 4}, func(x int64, _ int) string {
    return strconv.FormatInt(x, 10)
})
fmt.Println(lopMapData)
// []string{"1", "2", "3", "4"}

映射指定元素FilterMap

matching := lo.FilterMap([]string{"cpu", "gpu", "mouse", "keyboard"}, func(x string, _ int) (string, bool) {
    if strings.HasSuffix(x, "pu") {
        return "xpu", true
    }
    return "", false
})
fmt.Println(matching)
// []string{"xpu", "xpu"}

展开映射为多个元素

flatMap := lo.FlatMap([]int{0, 1, 2}, func(x int, _ int) []string {
    return []string{
        strconv.FormatInt(int64(x), 10),
        strconv.FormatInt(int64(x), 10),
    }
})
fmt.Println(flatMap)
// []string{"0", "0", "1", "1", "2", "2"}

聚合

Reduce ReduceRight

//从左侧聚合
reduce := lo.Reduce([]int{1, 2, 3, 4}, func(agg int, item int, _ int) int {
    return agg + item
}, 0)
fmt.Println(reduce)
// 10

//从右侧聚合
reduceRight := lo.ReduceRight([][]int{{0, 1}, {2, 3}, {4, 5}}, func(agg []int, item []int, _ int) []int {
    return append(agg, item...)
}, []int{})
fmt.Println(reduceRight)
// []int{4, 5, 2, 3, 0, 1}

遍历

lo.ForEach([]string{"hello", "world"}, func(x string, _ int) {
    println(x)
})
// prints "hello\nworld\n"

lop.ForEach([]string{"hello", "world"}, func(x string, _ int) {
    println(x)
})
// prints "hello\nworld\n" or "world\nhello\n"

分组、转字典

GroupBy 返回结果是字典,key是返回值,value是返回这个返回值的item

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

groups2 := lop.GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
    return i % 3
})
fmt.Println(groups2)
//map[0:[0 3] 1:[1 4] 2:[2 5]]

chunk 按照个数分组,返回二维切片

	chunk1 := lo.Chunk([]int{0, 1, 2, 3, 4, 5}, 2)
	fmt.Println(chunk1)
	// [[0 1] [2 3] [4 5]]

	chunk2 := lo.Chunk([]int{0, 1, 2, 3, 4, 5, 6}, 2)
	fmt.Println(chunk2)
	// [[0 1] [2 3] [4 5] [6]]

	chunk3 := lo.Chunk([]int{}, 2)
	fmt.Println(chunk3)
	// []

	chunk4 := lo.Chunk([]int{0}, 2)
	fmt.Println(chunk4)
	//[[0]]

PartitionBy 按照返回值分组,返回二维切片

partitions := lo.PartitionBy([]int{-2, -1, 0, 1, 2, 3, 4, 5}, func(x int) string {
    if x < 0 {
        return "negative"
    } else if x%2 == 0 {
        return "even"
    }
    return "odd"
})
fmt.Println(partitions)
// [[-2 -1] [0 2 4] [1 3 5]]

partitions2 := lop.PartitionBy([]int{-2, -1, 0, 1, 2, 3, 4, 5}, func(x int) string {
    if x < 0 {
        return "negative"
    } else if x%2 == 0 {
        return "even"
    }
    return "odd"
})
fmt.Println(partitions2)
// [[-2 -1] [0 2 4] [1 3 5]]

keyBy:根据返回值分组,返回map,返回值是key

m := lo.KeyBy([]string{"a", "aa", "aaa"}, func(str string) int {
    return len(str)
})
fmt.Println(m)
// map[int]string {1: "a", 2: "aa", 3: "aaa"}

characters := []Character{
    {dir: "left", code: 97},
    {dir: "right", code: 100},
}
result := lo.KeyBy(characters, func(char Character) string {
    return string(rune(char.code))
})
fmt.Println(result)
//map[a:{dir:left code:97} d:{dir:right code:100}]

Associate 将切片转换为map,key value都由指定方式生成

in := []*foo2{{baz: "apple", bar: 1}, {baz: "banana", bar: 2}}
aMap := lo.Associate(in, func(f *foo2) (string, int) {
    return f.baz, f.bar
})
fmt.Println(aMap)
// map[string][int]{ "apple":1, "banana":2 }

拍平切片

Flatten 将二维切片拍平

	flat := lo.Flatten([][]int{{0, 1}, {2, 3, 4, 5}})
	fmt.Println(flat)
	// []int{0, 1, 2, 3, 4, 5}

interleaved 将多个切片拍平

	interleaved := lo.Interleave([]int{1, 4, 7}, []int{2, 5, 8}, []int{3, 6, 9})
	fmt.Println(interleaved)
	// []int{1, 2, 3, 4, 5, 6, 7, 8, 9}

	interleaved1 := lo.Interleave([]int{1}, []int{2, 5, 8}, []int{3, 6}, []int{4, 7, 9, 10})
	fmt.Println(interleaved1)
	// []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	//即[1 2 3 4 5 6 7 8 9 10]

打乱顺序

Shuffle 打乱顺序

randomOrder := lo.Shuffle([]int{0, 1, 2, 3, 4, 5})
fmt.Println(randomOrder)
// []int{1, 4, 0, 3, 5, 2}

反转

Reverse

reverseOrder := lo.Reverse([]int{0, 1, 2, 3, 4, 5})
fmt.Println(reverseOrder)
// []int{5, 4, 3, 2, 1, 0}

填充、生成切片

用指定值填充切片,这个值需要是Clonable

//本来这个数组是{foo{"a"}, foo{"a"}},执行完函数变为{foo{"b"}, foo{"b"}}
initializedSlice := lo.Fill([]foo{foo{"a"}, foo{"a"}}, foo{"b"})
fmt.Println(initializedSlice)
// []foo{foo{"b"}, foo{"b"}}

//下面的是错误的,因为int64不是Clonable
intSlice := lo.Fill([]int64{1, 2}, 1)

repeat:用指定元素复制n份,生成切片

RepeatBy:执行指定函数n次,生成切片

slice1 := lo.Repeat(2, foo{"a"})
fmt.Println(slice1)
//[{a} {a}]

slice2 := lo.RepeatBy(0, func(i int) string {
    return strconv.FormatInt(int64(math.Pow(float64(i), 2)), 10)
})
fmt.Println(slice2)
// []string{}

slice3 := lo.RepeatBy(5, func(i int) string {
    return strconv.FormatInt(int64(math.Pow(float64(i), 2)), 10)
})
fmt.Println(slice3)
// []string{"0", "1", "4", "9", "16"}

times 多次执行某一函数,由返回值生成切片

	times := lo.Times(3, func(i int) string {
		return strconv.FormatInt(int64(i), 10)
	})
	fmt.Println(times)
	// []string{"0", "1", "2"}

	times2 := lop.Times(3, func(i int) string {
		return strconv.FormatInt(int64(i), 10)
	})
	fmt.Println(times2)
	// []string{"0", "1", "2"}

跳过某些元素

Drop DropRight 从左/右跳过某些元素

DropWhile DropRightWhile 从左/右开始执行函数,跳过函数返回值为true的,直到函数返回值为false时停止(注意:不会删除切片中所有满足条件的元素)

Reject 跳过函数中所有满足条件的元素

l := lo.Drop([]int{0, 1, 2, 3, 4, 5}, 2)
fmt.Println(l)
// []int{2, 3, 4, 5}

l2 := lo.DropRight([]int{0, 1, 2, 3, 4, 5}, 2)
fmt.Println(l2)
// []int{0, 1, 2, 3}

l3 := lo.DropWhile([]string{"a", "aa", "aaa", "aa", "aa"}, func(val string) bool {
    return len(val) <= 2
})
fmt.Println(l3)
// []string{"aaa", "aa", "aa"}

l4 := lo.DropRightWhile([]string{"a", "aa", "aaa", "aa", "aa"}, func(val string) bool {
    return len(val) <= 2
})
fmt.Println(l4)
// []string{"a", "aa", "aaa"}

odd := lo.Reject([]int{1, 2, 3, 4}, func(x int, _ int) bool {
    return x%2 == 0
})
fmt.Println(odd)
// []int{1, 3}

元素个数

Count,统计与指定值相等的元素个数

CountBy,统计满足条件的元素个数

CountValues:按元素值分组统计个数,返回字典,key是元素值,value是元素个数

CountValuesBy:按指定条件的返回值,统计元素个数,返回字典,key是返回值,value是元素个数

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

count2 := lo.CountBy([]int{1, 5, 1}, func(i int) bool {
    return i < 4
})
fmt.Println(count2)
// 2

CountValues

countValues1 := lo.CountValues([]int{})
fmt.Println(countValues1)
// map[int]int {}

countValues2 := lo.CountValues([]int{1, 2})
fmt.Println(countValues2)
// map[int]int {1: 1, 2: 1}

countValues3 := lo.CountValues([]int{1, 2, 2})
fmt.Println(countValues3)
// map[int]int{1: 1, 2: 2}

countValues4 := lo.CountValues([]string{"foo", "bar", ""})
fmt.Println(countValues4)
// map[string]int{"": 1, "foo": 1, "bar": 1}

countValues5 := lo.CountValues([]string{"foo", "bar", "bar"})
fmt.Println(countValues5)
// map[string]int{"foo": 1, "bar": 2}

CountValuesBy

isEven := func(v int) bool {
    return v%2 == 0
}

lo.CountValuesBy([]int{}, isEven)
// map[bool]int{}

lo.CountValuesBy([]int{1, 2}, isEven)
// map[bool]int{false: 1, true: 1}

lo.CountValuesBy([]int{1, 2, 2}, isEven)
// map[bool]int{false: 1, true: 2}

length := func(v string) int {
    return len(v)
}

lo.CountValuesBy([]string{"foo", "bar", ""}, length)
// map[int]int {0: 1, 3: 2}

lo.CountValuesBy([]string{"foo", "bar", "bar"}, length)
// map[int]int {3: 3}

子切片

Subset 指定起始位置和元素个数,返回切片。溢出不会抛异常。起始位置<0,代表从右往左数(-1开始),对应的正的起始位置=元素个数+起始位置(为负数时)

in2 := []int{0, 1, 2, 3, 4}

sub1 := lo.Subset(in2, 2, 3)
fmt.Println(sub1)
// []int{2, 3, 4}

sub2 := lo.Subset(in2, -4, 3)
fmt.Println(sub2)
// []int{1, 2, 3}

sub3 := lo.Subset(in2, -2, math.MaxUint)
fmt.Println(sub3)
// []int{3, 4}

Slice 返回切片,指定起始、终止下标,包括起始不包括终止。溢出不会抛异常。

in3 := []int{0, 1, 2, 3, 4}

slice11 := lo.Slice(in3, 0, 5)
fmt.Println(slice11)
// []int{0, 1, 2, 3, 4}

slice12 := lo.Slice(in3, 2, 3)
fmt.Println(slice12)
// []int{2}

slice13 := lo.Slice(in3, 2, 6)
fmt.Println(slice13)
// []int{2, 3, 4}

//终止位置<起始位置,截取不到
slice14 := lo.Slice(in3, 4, 3)
fmt.Println(slice14)
// []int{}

替换切片元素

Replace 替换切片中的元素,指定替换的个数(从左向右替换),个数<0时,全部替换。参数1是切片 2是旧值 3是新值 4是个数

in := []int{0, 1, 0, 1, 2, 3, 0}

slice1 := lo.Replace(in, 0, 42, 1)
fmt.Println(slice1)
// []int{42, 1, 0, 1, 2, 3, 0}

slice2 := lo.Replace(in, -1, 42, 1)
fmt.Println(slice2)
// []int{0, 1, 0, 1, 2, 3, 0}

slice3 := lo.Replace(in, 0, 42, 2)
fmt.Println(slice3)
// []int{42, 1, 42, 1, 2, 3, 0}

slice4 := lo.Replace(in, 0, 42, -1)
fmt.Println(slice4)
// []int{42, 1, 42, 1, 2, 3, 42}

ReplaceAll 替换所有指定元素的值

in := []int{0, 1, 0, 1, 2, 3, 0}

slice := lo.ReplaceAll(in, 0, 42)
fmt.Println(slice)
// []int{42, 1, 42, 1, 2, 3, 42}

slice1 := lo.ReplaceAll(in, -1, 42)
fmt.Println(slice1)
// []int{0, 1, 0, 1, 2, 3, 0}

删除零值、空字符串

Compact 对于字符串,删除所有空字符串

	in := []string{"", "foo", "", "bar", ""}

	slice := lo.Compact[string](in)
	fmt.Println(slice)
	// []string{"foo", "bar"}

排序

IsSorted 判断是否排序

IsSortedByKey 判断是否按照指定规则排序

slice := lo.IsSorted([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
fmt.Println(slice)
// true

slice := lo.IsSortedByKey([]string{"a", "bb", "ccc"}, func(s string) int {
    return len(s)
})
fmt.Println(slice)
// true

查找

IndexOf/LastIndexOf 返回数组中第一次/最后一次找到值的索引,如果找不到值则返回-1

Find 查找满足指定规则的元素,返回该元素及是否找到

FindIndexOf/FindLastIndexOf 查找满足指定规则的元素,返回该元素、第一次/最后一次出现的下标及是否找到,如果找不到值则返回-1

FindOrElse 查找满足指定规则的元素,找到返回该元素,否则返回指定值

found := lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 2)
// 2

notFound := lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 6)
// -1

found := lo.LastIndexOf([]int{0, 1, 2, 1, 2, 3}, 2)
// 4

notFound := lo.LastIndexOf([]int{0, 1, 2, 1, 2, 3}, 6)
// -1

str, ok := lo.Find([]string{"a", "b", "c", "d"}, func(i string) bool {
    return i == "b"
})
// "b", true

str, ok := lo.Find([]string{"foobar"}, func(i string) bool {
    return i == "b"
})
// "", false

str, index, ok := lo.FindIndexOf([]string{"a", "b", "a", "b"}, func(i string) bool {
    return i == "b"
})
// "b", 1, true

str, index, ok := lo.FindIndexOf([]string{"foobar"}, func(i string) bool {
    return i == "b"
})
// "", -1, false

str, index, ok := lo.FindLastIndexOf([]string{"a", "b", "a", "b"}, func(i string) bool {
    return i == "b"
})
// "b", 4, true

str, index, ok := lo.FindLastIndexOf([]string{"foobar"}, func(i string) bool {
    return i == "b"
})
// "", -1, false

str1 := lo.FindOrElse([]string{"a", "b", "c", "d"}, "x", func(i string) bool {
    return i == "b"
})
fmt.Println(str1)
// "b"

str2 := lo.FindOrElse([]string{"foobar"}, "x", func(i string) bool {
    return i == "b"
})
fmt.Println(str2)
// "x"

Min/Max 查找最小/最大值,找不到返回零值

MinBy/MaxBy 按照指定比较函数查找最小/最大值,集合是空返回零值,找到重复值返回第一次出现的值

min := lo.Min([]int{1, 2, 3})
// 1

min := lo.Min([]int{})
// 0

min := lo.MinBy([]string{"s1", "string2", "s3"}, func(item string, min string) bool {
    return len(item) < len(min)
})
// "s1"

min := lo.MinBy([]string{}, func(item string, min string) bool {
    return len(item) < len(min)
})
// ""

max := lo.Max([]int{1, 2, 3})
// 3

max := lo.Max([]int{})
// 0

max := lo.MaxBy([]string{"string1", "s2", "string3"}, func(item string, max string) bool {
    return len(item) > len(max)
})
// "string1"

max := lo.MaxBy([]string{}, func(item string, max string) bool {
    return len(item) > len(max)
})
// ""

Last 查找最后元素的值,为空err有值

Nth 第n个元素的值,n为负数时从右数,n超出err

Sample 随机取一个值,

Samples 取n个随机唯一值

last, err := lo.Last([]int{1, 2, 3})
// 3

nth, err := lo.Nth([]int{0, 1, 2, 3}, 2)
// 2

nth, err := lo.Nth([]int{0, 1, 2, 3}, -2)
// 2

lo.Sample([]string{"a", "b", "c"})
// a random string from []string{"a", "b", "c"}

lo.Sample([]string{})
// ""

lo.Samples([]string{"a", "b", "c"}, 3)
// []string{"a", "b", "c"} in random order

字典操作

名称函数描述
Kys Values获取所有键、所有值,返回切片
ValueOr获取指定键的值,键不存在时,返回默认值

获取所有键、所有值

Keys Values 获取所有键、所有值,返回切片

keys := lo.Keys[string, int](map[string]int{"foo": 1, "bar": 2})
fmt.Print(keys)
// []string{"foo", "bar"}

values := lo.Values[string, int](map[string]int{"foo": 1, "bar": 2})
fmt.Print(values)
// []int{1, 2}

获取指定键的值

ValueOr 获取指定键的值,键不存在时,返回默认值

	value1 := lo.ValueOr[string, int](map[string]int{"foo": 1, "bar": 2}, "foo", 42)
	fmt.Print(value1)
	// 1

	value2 := lo.ValueOr[string, int](map[string]int{"foo": 1, "bar": 2}, "baz", 42)
	fmt.Print(value2)
	// 42

筛选

PickBy 获取满足指定条件的键值对

PickByKeys PickByValues 获取指定key或value的键值对

OmitBy 跳过满足指定条件的键值对

OmitByKeys OmitByValues跳过指定key value的键值对

m := lo.PickBy(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(key string, value int) bool {
    return value%2 == 1
})
fmt.Print(m)
// map[string]int{"foo": 1, "baz": 3}

m1 := lo.PickByKeys(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []string{"foo", "baz"})
fmt.Print(m1)
// map[string]int{"foo": 1, "baz": 3}

m2 := lo.PickByValues(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []int{1, 3})
fmt.Print(m2)
// map[string]int{"foo": 1, "baz": 3}

m := lo.OmitBy(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(key string, value int) bool {
    return value%2 == 1
})
fmt.Print(m)
// map[string]int{"bar": 2}

m1 := lo.OmitByKeys(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []string{"foo", "baz"})
fmt.Print(m1)
// map[string]int{"bar": 2}

m2 := lo.OmitByValues(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []int{1, 3})
fmt.Print(m2)
// map[string]int{"bar": 2}

与键值对切片转换

注意,lo.Entry[string, int]是键值对类型(字典中的一个键值对),键值对是lo包中定义的类型

Entries:将键值对字典转为键值对数组

FromEntries:将键值对数组转为键值对字典

item := map[string]int{"foo": 1, "bar": 2}
entries := lo.Entries(item)
// []lo.Entry[string, int]{
//     {
//         Key: "foo",
//         Value: 1,
//     },
//     {
//         Key: "bar",
//         Value: 2,
//     },
// }
fmt.Println(item)
fmt.Println(entries)

m := lo.FromEntries([]lo.Entry[string, int]{
    {
        Key: "foo",
        Value: 1,
    },
    {
        Key: "bar",
        Value: 2,
    },
})
// map[string]int{"foo": 1, "bar": 2}

反转

Invert 创建由反向键和值组成的映射。如果map包含重复值,则后续值将覆盖前一个值的属性赋值

m1 := lo.Invert(map[string]int{"a": 1, "b": 2})
fmt.Print(m1)
// map[int]string{1: "a", 2: "b"}

m2 := lo.Invert(map[string]int{"a": 1, "b": 2, "c": 1})
fmt.Print(m2)
// map[int]string{1: "c", 2: "b"}

合并

Assign 从左到右合并多个键值对

mergedMaps := lo.Assign[string, int](
    map[string]int{"a": 1, "b": 2},
    map[string]int{"b": 3, "c": 4},
)
fmt.Print(mergedMaps)
// map[string]int{"a": 1, "b": 3, "c": 4}

映射

MapKeys、MapValues:将键/值映射为其他类型

MapEntries:生成新的键值对

m1 := lo.MapKeys(map[int]int{1: 1, 2: 2, 3: 3, 4: 4}, func(_ int, v int) string {
    return strconv.FormatInt(int64(v), 10)
})
fmt.Print(m1)
// map[string]int{"1": 1, "2": 2, "3": 3, "4": 4}

m := map[int]int64{1: 1, 2: 2, 3: 3}
m2 := lo.MapValues(m, func(x int64, _ int) string {
    return strconv.FormatInt(x, 10)
})
fmt.Print(m2)
// map[int]string{1: "1", 2: "2", 3: "3"}

in := map[string]int{"foo": 1, "bar": 2}
out := lo.MapEntries(in, func(k string, v int) (int, string) {
    return v,k
})
fmt.Print(out)
// map[int]string{1: "foo", 2: "bar"}

MapToSlice 映射为数组(将一个键值对转换为一个普通值)

m3 := map[int]int64{1: 4, 2: 5, 3: 6}
s := lo.MapToSlice(m3, func(k int, v int64) string {
    return fmt.Sprintf("%d_%d", k, v)
})
fmt.Print(s)
// []string{"1_4", "2_5", "3_6"}

查找、最大最小值、指定位置的值

FindKey:根据值查找满足条件的键值对,返回key、是否找到

FindKeyBy:根据指定规则查找满足条件的键值对,返回key、是否找到

result1, ok1 := lo.FindKey(map[string]int{"foo": 1, "bar": 2, "baz": 3}, 2)
// "bar", true

result2, ok2 := lo.FindKey(map[string]int{"foo": 1, "bar": 2, "baz": 3}, 42)
// "", false

type test struct {
    foobar string
}
result3, ok3 := lo.FindKey(map[string]test{"foo": test{"foo"}, "bar": test{"bar"}, "baz": test{"baz"}}, test{"foo"})
// "foo", true

result1, ok1 := lo.FindKeyBy(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(k string, v int) bool {
    return k == "foo"
})
// "foo", true

result2, ok2 := lo.FindKeyBy(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(k string, v int) bool {
    return false
})
// "", false

数学操作

创建数字序列

Range 创建从0开始指定元素个数的序列

RangeFrom 创建指定起始位置、元素个数的序列

RangeWithSteps 创建指定起始位置、步长、元素个数的序列

result8 := lo.Range(0)
fmt.Print(result8)
// []

result1 := lo.Range(4)
fmt.Print(result1)
// [0, 1, 2, 3]

result2 := lo.Range(-4)
fmt.Print(result2)
// [0, -1, -2, -3]

result3 := lo.RangeFrom(1, 5)
fmt.Print(result3)
// [1, 2, 3, 4, 5]

result4 := lo.RangeFrom[float64](1.0, 5)
fmt.Print(result4)
// [1.0, 2.0, 3.0, 4.0, 5.0]

result5 := lo.RangeWithSteps(0, 20, 5)
fmt.Print(result5)
// [0, 5, 10, 15]

result6 := lo.RangeWithSteps[float32](-1.0, -4.0, -1.0)
fmt.Print(result6)
// [-1.0, -2.0, -3.0]

result7 := lo.RangeWithSteps(1, 4, -1)
fmt.Print(result7)
// []

取中位数

Clamp

r1 := lo.Clamp(0, -10, 10)
fmt.Print(r1)
// 0

r2 := lo.Clamp(-42, -10, 10)
fmt.Print(r2)
// -10

r3 := lo.Clamp(42, -10, 10)
fmt.Print(r3)
// 10

求和

Sum 元素求和

SumBy 元素执行函数后,结果求和

list := []int{1, 2, 3, 4, 5}
sum := lo.Sum(list)
fmt.Print(sum)
// 15

strings := []string{"foo", "bar"}
sum2 := lo.SumBy(strings, func(item string) int {
    return len(item)
})
fmt.Print(sum2)
// 6

字符串操作

生成随机字符串

RandomString 根据指定字符集生成指定长度的字符串

	str := lo.RandomString(5, lo.LettersCharset)
	fmt.Print(str)
	// example: "eIGbt"

裁剪

Substring 返回指定起始位置、指定长度的字符串

	sub := lo.Substring("hello", 2, 3)
	fmt.Print(sub)
	// "llo"

	sub1 := lo.Substring("hello", -4, 3)
	fmt.Print(sub1)
	// "ell"

	sub2 := lo.Substring("hello", -2, math.MaxUint)
	fmt.Print(sub2)
	// "lo"

分组

ChunkString 从左到右,按指定个数分组

	lo.ChunkString("123456", 2)
	// []string{"12", "34", "56"}

	lo.ChunkString("1234567", 2)
	// []string{"12", "34", "56", "7"}

	lo.ChunkString("", 2)
	// []string{""}

	lo.ChunkString("1", 2)
	// []string{"1"}

字符个数

RuneLength 返回字符个数,等同于utf8.RuneCountInString

sub := lo.RuneLength("hellô")
// 5

sub := len("hellô")
// 6

元组操作

go语言应该不支持元组,这个只是软件包的行为

type Tuple2[A any, B any] struct {
	A A
	B B
}

多个值与元组转换

T2 T3 T4 T5 T6 T7 T8 T9:代表具有不同的参数个数

	tuple1 := lo.T2("x", 1)
	fmt.Println(tuple1)
	// Tuple2[string, int]{A: "x", B: 1}
	//实际输出{x 1}

	tuple2 := lo.T2(example())
	fmt.Println(tuple2)
	// Tuple2[string, int]{A: "y", B: 2}
	//实际输出{y 2}

Unpack2...9:解析指定个数的

Unpack:解析任意个数的

	r1, r2 := lo.Unpack2(lo.Tuple2[string, int]{"a", 1})
	fmt.Println(r1, r2)
	// "a", 1

	tuple2 := lo.T2("a", 1)
	a, b := tuple2.Unpack()
	fmt.Println(a, b)
	// "a" 1

切片与元组转换

Zip2...9 将多个普通切片转换为元组切片

	tuples := lo.Zip2([]string{"a", "b"}, []int{1, 2})
	fmt.Println(tuples)
	// []Tuple2[string, int]{{A: "a", B: 1}, {A: "b", B: 2}}

Unzip2...9 将元组切片转换为多个普通切片

	a, b := lo.Unzip2([]lo.Tuple2[string, int]{{A: "a", B: 1}, {A: "b", B: 2}})
	fmt.Println(a, b)
	// []string{"a", "b"}
	// []int{1, 2}

切片交集操作

检查切片中的元素

Contains 是否包含指定元素

Contains 是否包含指定规则的元素

Every 是否包含子集的所有元素,子集为空,始终返回true

EveryBy 是否每个元素都满足指定规则,集合为空始终返回true

Some 如果集合中至少包含子集的一个元素,则返回true。如果子集为空,则Some返回false

SomeBy 是否包含指定规则的元素,集合空返回false

None 是否不包含子集的任何元素,子集空返回true

NoneBy 是否任何元素都不满足指定规则,集合为空返回true

present := lo.Contains([]int{0, 1, 2, 3, 4, 5}, 5)
// true

present := lo.ContainsBy([]int{0, 1, 2, 3, 4, 5}, func(x int) bool {
    return x == 3
})
// true

ok := lo.Every([]int{0, 1, 2, 3, 4, 5}, []int{0, 2})
// true

ok := lo.Every([]int{0, 1, 2, 3, 4, 5}, []int{0, 6})
// false

b := EveryBy([]int{1, 2, 3, 4}, func(x int) bool {
    return x < 5
})
// true

ok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{0, 2})
// true

ok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6})
// false

b := SomeBy([]int{1, 2, 3, 4}, func(x int) bool {
    return x < 3
})
// true

b := None([]int{0, 1, 2, 3, 4, 5}, []int{0, 2})
// false
b := None([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6})
// true

b := NoneBy([]int{1, 2, 3, 4}, func(x int) bool {
    return x < 0
})
// true

交集、并集、差集

Intersect 取两个切片的交集

result1 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{0, 2})
// []int{0, 2}

result2 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{0, 6})
// []int{0}

result3 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6})
// []int{}

Union 多个集合的并集

union := lo.Union([]int{0, 1, 2, 3, 4, 5}, []int{0, 2}, []int{0, 10})
// []int{0, 1, 2, 3, 4, 5, 10}

Difference 两个集合返回另一集合中没有的元素

Without 集合中排除指定值的元素

WithoutEmpty 集合中排除零值的元素

left, right := lo.Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 2, 6})
// []int{1, 3, 4, 5}, []int{6}

left, right := lo.Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 1, 2, 3, 4, 5})
// []int{}, []int{}

subset := lo.Without([]int{0, 2, 10}, 2)
// []int{0, 10}

subset := lo.Without([]int{0, 2, 10}, 0, 1, 2, 3, 4, 5)
// []int{10}

subset := lo.WithoutEmpty([]int{0, 2, 10})
// []int{2, 10}

条件操作

Ternary 满足参数1条件返回参数2,否则返回参数3

TernaryF 满足参数1条件执行参数2函数,否则执行参数3函数

result := lo.Ternary(true, "a", "b")
// "a"

result := lo.Ternary(false, "a", "b")
// "b"

result := lo.TernaryF(true, func() string { return "a" }, func() string { return "b" })
// "a"

result := lo.TernaryF(false, func() string { return "a" }, func() string { return "b" })
// "b"

//可用于避免初始化时的空指针解引用,或避免运行不必要的代码
var s *string
someStr := TernaryF(s == nil, func() string { return uuid.New().String() }, func() string { return *s })
// ef782193-c30c-4e2e-a7ae-f8ab5e125e02

If / ElseIf / Else 链式调用,前两个判断指名条件和返回值,最后一个指名默认返回值

IfF / ElseIfF / ElseF 链式调用,前两个判断指名条件和返回值函数,最后一个指名默认返回值函数

result := lo.If(true, 1).
    ElseIf(false, 2).
    Else(3)
// 1

result := lo.If(false, 1).
    ElseIf(true, 2).
    Else(3)
// 2

result := lo.If(false, 1).
    ElseIf(false, 2).
    Else(3)
// 3

result := lo.IfF(true, func () int {
        return 1
    }).
    ElseIfF(false, func () int {
        return 2
    }).
    ElseF(func () int {
        return 3
    })
// 1

//混用
result := lo.IfF(true, func () int {
        return 1
    }).
    Else(42)
// 1

Switch/Case/Default:链式调用,Case指名默认条件值、返回值

Switch/CaseF/DefaultF:链式调用,CaseF指名默认条件值、调用函数

result := lo.Switch(1).
    Case(1, "1").
    Case(2, "2").
    Default("3")
// "1"

result := lo.Switch(2).
    Case(1, "1").
    Case(2, "2").
    Default("3")
// "2"

result := lo.Switch(42).
    Case(1, "1").
    Case(2, "2").
    Default("3")
// "3"

result := lo.Switch(1).
    CaseF(1, func() string {
        return "1"
    }).
    CaseF(2, func() string {
        return "2"
    }).
    DefaultF(func() string {
        return "3"
    })
// "1"

//混用
result := lo.Switch(1).
    CaseF(1, func() string {
        return "1"
    }).
    Default("42")
// "1"

类型操作

取指针

ToPtr:取指针

EmptyableToPtr:如果value为非零,则返回value的指针副本。否则,返回nil指针

FromPtr:返回指针指向的值,指针是空时返回零值

FromPtrOr:返回指针指向的值,指针是空时返回指定值

ToSlicePtr:返回值的指针副本的切片。

ptr := lo.ToPtr("hello world")
// *string{"hello world"}

ptr := lo.EmptyableToPtr[[]int](nil)
// nil

ptr := lo.EmptyableToPtr[string]("")
// nil

ptr := lo.EmptyableToPtr[[]int]([]int{})
// *[]int{}

ptr := lo.EmptyableToPtr[string]("hello world")
// *string{"hello world"}

str := "hello world"
value := lo.FromPtr(&str)
// "hello world"

value := lo.FromPtr[string](nil)
// ""

str := "hello world"
value := lo.FromPtrOr(&str, "empty")
// "hello world"

value := lo.FromPtrOr[string](nil, "empty")
// "empty"

ptr := lo.ToSlicePtr([]string{"hello", "world"})
// []*string{"hello", "world"}

转类型

ToAnySlice 将切片转为任意类型

FromAnySlice:将any类型切片所有元素转成一个类型,返回结果、是否成功

elements := lo.ToAnySlice([]int{1, 5, 1})
// []any{1, 5, 1}

elements, ok := lo.FromAnySlice([]any{"foobar", 42})
// []string{}, false

elements, ok := lo.FromAnySlice([]any{"foobar", "42"})
// []string{"foobar", "42"}, true

取零值

Empty:返回一个类型的零值

IsEmpty/IsNotEmpty:判断一个数据是否是/不是一个类型的零值

Coalesce:返回第一个非零值的数据、是否含有

lo.Empty[int]()
// 0
lo.Empty[string]()
// ""
lo.Empty[bool]()
// false

lo.IsEmpty(0)
// true
lo.IsEmpty(42)
// false

lo.IsEmpty("")
// true
lo.IsEmpty("foobar")
// false

type test struct {
    foobar string
}

lo.IsEmpty(test{foobar: ""})
// true
lo.IsEmpty(test{foobar: "foobar"})
// false

lo.IsNotEmpty(0)
// false
lo.IsNotEmpty(42)
// true

lo.IsNotEmpty("")
// false
lo.IsNotEmpty("foobar")
// true

type test struct {
    foobar string
}

lo.IsNotEmpty(test{foobar: ""})
// false
lo.IsNotEmpty(test{foobar: "foobar"})
// true

result, ok := lo.Coalesce(0, 1, 2, 3)
// 1 true

result, ok := lo.Coalesce("")
// "" false

var nilStr *string
str := "foobar"
result, ok := lo.Coalesce[*string](nil, nilStr, &str)
// &"foobar" true

函数操作

Partial:将一个函数转为只需要1个参数的函数,原函数后面的参数使用指定值

Partial2...5:将一个函数转为只需要2..5个参数的函数,原函数后面的参数使用指定值

add := func(x, y int) int { return x + y }
f := lo.Partial(add, 5)

f(10)
// 15

f(42)
// 47

add := func(x, y, z int) int { return x + y + z }
f := lo.Partial2(add, 42)

f(10, 5)
// 57

f(42, -4)
// 80

并行操作

Attempt:迭代一个函数,指定最大迭代次数,当函数不返回nil或error时,返回迭代次数。否则,返回最大迭代次数、err。当最大迭代次数<=0时,代表可以无限迭代。

AttemptWithDelay:同Attempt,只不过每次迭代会有延时,结果会延时时间返回

AttemptWhile:同Attempt,只不过迭代函数具有两个返回值,当迭代函数的参数2返回false时,将立即终止调用

AttemptWhileWithDelay:同AttemptWhile,只不过每次迭代由延时

iter, err := lo.Attempt(42, func(i int) error {
    if i == 5 {
        return nil
    }

    return fmt.Errorf("failed")
})
// 6
// nil

iter, err := lo.Attempt(2, func(i int) error {
    if i == 5 {
        return nil
    }

    return fmt.Errorf("failed")
})
// 2
// error "failed"

iter, err := lo.Attempt(0, func(i int) error {
    if i < 42 {
        return fmt.Errorf("failed")
    }

    return nil
})
// 43
// nil

iter, duration, err := lo.AttemptWithDelay(5, 2*time.Second, func(i int, duration time.Duration) error {
    if i == 2 {
        return nil
    }

    return fmt.Errorf("failed")
})
// 3
// ~ 4 seconds
// nil

count1, err1 := lo.AttemptWhile(5, func(i int) (error, bool) {
    err := doMockedHTTPRequest(i)
    if err != nil {
        if errors.Is(err, ErrBadRequest) { // lets assume ErrBadRequest is a critical error that needs to terminate the invoke
            return err, false // flag the second return value as false to terminate the invoke
        }

        return err, true
    }

    return nil, false
})

count1, time1, err1 := lo.AttemptWhileWithDelay(5, time.Millisecond, func(i int, d time.Duration) (error, bool) {
    err := doMockedHTTPRequest(i)
    if err != nil {
        if errors.Is(err, ErrBadRequest) { // lets assume ErrBadRequest is a critical error that needs to terminate the invoke
            return err, false // flag the second return value as false to terminate the invoke
        }

        return err, true
    }

    return nil, false
})

Debounce:lo.NewDebounce会绑定一个函数并指定延迟时间,调用debounce触发此函数(带延迟),调用cancel会取消

f := func() {
    println("Called once after 100ms when debounce stopped invoking!")
}

debounce, cancel := lo.NewDebounce(100 * time.Millisecond, f)
for j := 0; j < 10; j++ {
    debounce()
}

time.Sleep(1 * time.Second)
cancel()
//Called once after 100ms when debounce stopped invoking!

f := func(key string, count int) {
    println(key + ": Called once after 100ms when debounce stopped invoking!")
}

debounce, cancel := lo.NewDebounceBy(100 * time.Millisecond, f)
for j := 0; j < 10; j++ {
    debounce("first key")
    debounce("second key")
}

time.Sleep(1 * time.Second)
cancel("first key")
cancel("second key")
//second key: Called once after 100ms when debounce stopped invoking!
//first key: Called once after 100ms when debounce stopped invoking!

Synchronize:将底层回调封装在互斥对象中。它接收一个可选的互斥锁。

s := lo.Synchronize()

for i := 0; i < 10; i++ {
    go s.Do(func () {
        println("will be called sequentially")
    })
}

//相当于下面的写法
mu := sync.Mutex{}

func foobar() {
    mu.Lock()
    defer mu.Unlock()

    // ...
}

通道

Async:在程序中执行函数,并在通道中返回结果

ch := lo.Async(func() error { time.Sleep(10 * time.Second); return nil })
// chan error (nil)

Async0-6:在程序中执行函数,并在通道中返回结果。对于具有多个返回值的函数,结果将作为通道内的元组返回。对于没有return的函数,struct{}将在通道中返回

ch := lo.Async0(func() { time.Sleep(10 * time.Second) })
// chan struct{}

ch := lo.Async1(func() int {
  time.Sleep(10 * time.Second);
  return 42
})
// chan int (42)

ch := lo.Async2(func() (int, string) {
  time.Sleep(10 * time.Second);
  return 42, "Hello"
})
// chan lo.Tuple2[int, string] ({42, "Hello"})

事务

NewTransaction().Then().Then()触发下一步,返回transaction实例,transaction.Process()方法调用上述步骤

实现一个Saga模式,即拆分为多个步骤,每个步骤执行完成会触发下一个步骤,当某个步骤执行错误时,会向前回滚。

transaction := NewTransaction[int]().
    Then(
        func(state int) (int, error) {
            fmt.Println("step 1")
            return state + 10, nil
        },
        func(state int) int {
            fmt.Println("rollback 1")
            return state - 10
        },
    ).
    Then(
        func(state int) (int, error) {
            fmt.Println("step 2")
            return state + 15, nil
        },
        func(state int) int {
            fmt.Println("rollback 2")
            return state - 15
        },
    ).
    Then(
        func(state int) (int, error) {
            fmt.Println("step 3")

            if true {
                return state, fmt.Errorf("error")
            }

            return state + 42, nil
        },
        func(state int) int {
            fmt.Println("rollback 3")
            return state - 42
        },
    )

_, _ = transaction.Process(-5)

// Output:
// step 1
// step 2
// step 3
// rollback 2
// rollback 1

错误处理

Validate:当不满足条件时创建错误的辅助函数。参数1是判断条件,参数2是提示信息,参数3是要判断的元素

slice := []string{"a"}
val := lo.Validate(len(slice) == 0, "Slice should be empty but contains %v", slice)
// error("Slice should be empty but contains [a]")

slice := []string{}
val := lo.Validate(len(slice) == 0, "Slice should be empty but contains %v", slice)
// nil

Must 如果第二个参数为error或false,将函数调用包装为panic,否则返回值。相当于把原函数最后一个标志位error或bool值吞掉,只返回数据值或者panic异常

Must0-6 同Must,只是包装的函数返回多个值,最后一个返回值是error

//Parse函数返回第一个值为解析结果、第二个值为error
val := lo.Must(time.Parse("2006-01-02", "2022-01-15"))
// 2022-01-15

val := lo.Must(time.Parse("2006-01-02", "bad-value"))
// panics

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())

//可以包装形如func (...) (..., ok bool)的函数,最后一个返回值可以是bool
// math.Signbit(float64) bool
lo.Must0(math.Signbit(v))

// bytes.Cut([]byte,[]byte) ([]byte, []byte, bool)
before, after := lo.Must2(bytes.Cut(s, sep))

//可以通过添加一些类似print的参数来为panic消息提供上下文
//这时第一个参数是bool值,为false时输出后面的提示信息
val, ok := lo.Find(myString, func(i string) bool {
    return i == requiredChar
})
lo.Must0(ok, "'%s' must always contain '%s'", myString, requiredChar)

list := []int{0, 1, 2}
item := 5	//lo.Contains返回bool值
lo.Must0(lo.Contains[int](list, item), "'%s' must always contain '%s'", list, item)
...

Try 调用函数,返回bool值指示是否成功。当函数内部有异常时,返回false

Try0...6 同上,但回调函数返回0...6个值

TryOr:回调函数出错时,返回指定值

TryOr2...6 同上,但回调函数返回0...6个值

TryWithErrorValue:与Try的行为相同,但也会返回传递给panic的值

TryCatch:当捕获到异常时,执行另一回调函数

TryCatchWithErrorValue:当捕获到异常时,执行另一回调函数,会将异常参数传给异常处理回调函数

ok := lo.Try(func() error {
    panic("error")
    return nil
})
// false

ok := lo.Try(func() error {
    return nil
})
// true

ok := lo.Try(func() error {
    return fmt.Errorf("error")
})
// false

ok := lo.Try2(func() (string, error) {
    panic("error")
    return "", nil
})
// false

str, ok := lo.TryOr(func() (string, error) {
    panic("error")
    return "hello", nil
}, "world")
// world
// false

str, ok := lo.TryOr(func() error {
    return "hello", nil
}, "world")
// hello
// true

str, ok := lo.TryOr(func() error {
    return "hello", fmt.Errorf("error")
}, "world")
// world
// false

str, nbr, ok := lo.TryOr2(func() (string, int, error) {
    panic("error")
    return "hello", 42, nil
}, "world", 21)
// world
// 21
// false

err, ok := lo.TryWithErrorValue(func() error {
    panic("error")
    return nil
})
// "error", false

caught := false
ok := lo.TryCatch(func() error {
    panic("error")
    return nil
}, func() {
    caught = true
})
// false
// caught == true

caught := false
ok := lo.TryCatchWithErrorValue(func() error {
    panic("error")
    return nil
}, func(val any) {
    caught = val == "error"
})
// false
// caught == true

ErrorsAs:将错误转换为指定类型的错误,返回结果、是否转换成功

err := doSomething()

if rateLimitErr, ok := lo.ErrorsAs[*RateLimitError](err); ok {
    // retry later
}


err := doSomething()

var rateLimitErr *RateLimitError
//errors.As将一个错误转换为指定类型的错误,并返回是否转换成功
if ok := errors.As(err, &rateLimitErr); ok {
    // retry later
}

信道操作

消息分发

ChannelDispatcher 将来自输入通道的消息分发到N个子通道。关闭事件被传播给子事件。底层通道可以具有固定的缓冲容量,也可以在cap为0时不进行缓冲。

//创建一个指定容量的通道
ch := make(chan int, 42)
//向通道发送一些数据
for i := 0; i <= 10; i++ {
    ch <- i
}

//ChannelDispatcher,将通道数据转发给5个指定容量的只能接收数据的通道,返回这些通道的切片。可以指定分发策略
//lo.DispatchingStrategyRoundRobin[int]是分发策略,还有别的,见官网
children := lo.ChannelDispatcher(ch, 5, 10, lo.DispatchingStrategyRoundRobin[int])
// []<-chan int{...}

consumer := func(c <-chan int) {
    for {
        //从通道取数据并打印出来
        msg, ok := <-c
        if !ok {
            println("closed")

            break
        }

        println(msg)
    }
}

for i := range children {
    go consumer(children[i])
}

切片与通道转换

SliceToChannel:返回集合元素的只读通道。通道在最后一个元素之后关闭。通道容量可自定义

ChannelToSlice:将通道转换为切片

list := []int{1, 2, 3, 4, 5}

for v := range lo.SliceToChannel(2, list) {
    println(v)
}
// prints 1, then 2, then 3, then 4, then 5

list := []int{1, 2, 3, 4, 5}
ch := lo.SliceToChannel(2, list)

items := ChannelToSlice(ch)
// []int{1, 2, 3, 4, 5}

Generator:实现生成器设计模式。通道在最后一个元素之后关闭。通道容量可自定义。应该是遍历元素时,才会从generator中获取元素

generator := func(yield func(int)) {
    yield(1)
    yield(2)
    yield(3)
}

//
for v := range lo.Generator(2, generator) {
    println(v)
}
// prints 1, then 2, then 3

Buffer 从通道创建一个包含n个元素的切片。返回片、片长度、读取时间和通道状态(打开/关闭)

ch := lo.SliceToChannel(2, []int{1, 2, 3, 4, 5})

//从通道中取出3个数,通道还剩下两个数,通道仍是开启状态
items1, length1, duration1, ok1 := lo.Buffer(ch, 3)
// []int{1, 2, 3}, 3, 0s, true

//从通道中取出剩下的2个数,通道关闭
items2, length2, duration2, ok2 := lo.Buffer(ch, 3)
// []int{4, 5}, 2, 0s, false

//示例,从rabbitMq读取
ch := readFromQueue()

for {
    // read 1k items
    items, length, _, ok := lo.Buffer(ch, 1000)

    // do batching stuff

    if !ok {
        break
    }
}

BufferWithTimeout 从通道创建一个包含n个元素的切片,并带有超时。返回片、片长度、读取时间和通道状态(打开/关闭)

generator := func(yield func(int)) {
    for i := 0; i < 5; i++ {
        yield(i)
        time.Sleep(35*time.Millisecond)
    }
}

ch := lo.Generator(0, generator)

//因为超时了,所以只取得两个
items1, length1, duration1, ok1 := lo.BufferWithTimeout(ch, 3, 100*time.Millisecond)
// []int{1, 2}, 2, 100ms, true

items2, length2, duration2, ok2 := lo.BufferWithTimeout(ch, 3, 100*time.Millisecond)
// []int{3, 4, 5}, 3, 75ms, true

items3, length3, duration2, ok3 := lo.BufferWithTimeout(ch, 3, 100*time.Millisecond)
// []int{}, 0, 10ms, false

//示例,从rabbitMq中取消息
ch := readFromQueue()
for {
    // read 1k items
    // wait up to 1 second
    items, length, _, ok := lo.BufferWithTimeout(ch, 1000, 1*time.Second)

    // do batching stuff

    if !ok {
        break
    }
}

//示例,从rabbitMq中多线程取消息
ch := readFromQueue()
// 5 workers
// prefetch 1k messages per worker
children := lo.ChannelDispatcher(ch, 5, 1000, lo.DispatchingStrategyFirst[int])
consumer := func(c <-chan int) {
    for {
        // read 1k items
        // wait up to 1 second
        items, length, _, ok := lo.BufferWithTimeout(ch, 1000, 1*time.Second)

        // do batching stuff

        if !ok {
            break
        }
    }
}

for i := range children {
    go consumer(children[i])
}

多个通道收取/广播消息

FanIn 将来自多个输入通道的消息合并到单个缓冲通道中。输出消息没有优先级。当所有上游通道达到EOF时,下游通道关闭

FanOut 将所有上游消息广播到多个下游通道。当上游通道达到EOF时,下游通道关闭。如果任何下游通道已满,则暂停广播

stream1 := make(chan int, 42)
stream2 := make(chan int, 42)
stream3 := make(chan int, 42)

all := lo.FanIn(100, stream1, stream2, stream3)
// <-chan int

stream := make(chan int, 42)
all := lo.FanOut(5, 100, stream)
// [5]<-chan int

排序

  1. sort 包:提供了对切片排序的函数。
    • 可以使用 sort.Slice 函数进行自定义排序。
    • 示例代码:
package main

import (
    "fmt"
    "sort"
)

type Person struct {
    Name string
    Age  int
}

func main() {
    people := []Person{
        {"Alice", 25},
        {"Bob", 30},
        {"Charlie", 20},
    }

    sort.Slice(people, func(i, j int) bool {
        return people[i].Age < people[j].Age
    })

    fmt.Println(people)
}