samber/lo - Go 泛型工具库:核心模块文档
概述
本文档是 samber/lo 核心包(lo)的完整参考,包含 300+ 个不可变工具函数,涵盖切片、映射、通道、字符串、数学、并发、错误处理等常见操作。所有函数均基于 Go 1.18+ 泛型实现,类型安全。
源文档:https://lo.samber.dev/docs/core 官方仓库:https://github.com/samber/lo
切片操作
Count
统计集合中等于给定值的元素数量。
lo.Count([]int{1, 5, 1}, 1) // 2
func Count[T comparable](collection []T, value T) intCountBy
统计集合中使谓词函数返回 true 的元素数量。
lo.CountBy([]int{1, 5, 1}, func(i int) bool {
return i < 4
}) // 2
func CountBy[T any](collection []T, predicate func(item T) bool) intCountValues
统计集合中每个元素出现的次数。
lo.CountValues([]int{1, 2, 2})
// map[int]int{1: 1, 2: 2}
func CountValues[T comparable](collection []T) map[T]intCountValuesBy
统计每个转换后值出现的次数(等价于先 Map 再 CountValues)。
isEven := func(v int) bool { return v%2 == 0 }
lo.CountValuesBy([]int{1, 2, 2}, isEven)
// map[bool]int{false: 1, true: 2}
func CountValuesBy[T any, U comparable](collection []T, transform func(item T) U) map[U]intFilter
遍历集合,返回所有使谓词函数返回 true 的元素组成的切片。
even := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {
return x%2 == 0
}) // []int{2, 4}
func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) SliceMap
使用函数将切片中的每个元素转换为新类型。
transformed := lo.Map([]int64{1, 2, 3, 4}, func(x int64, index int) string {
return strconv.FormatInt(x, 10)
})
// []string{"1", "2", "3", "4"}
func Map[T any, R any](collection []T, transform func(item T, index int) R) []RFlatMap
转换切片并将其展平为另一类型的切片。
out := lo.FlatMap([]int64{0, 1, 2}, func(x int64, _ int) []string {
return []string{strconv.FormatInt(x, 10), strconv.FormatInt(x, 10)}
})
// []string{"0", "0", "1", "1", "2", "2"}
func FlatMap[T any, R any](collection []T, transform func(item T, index int) []R) []RFilterMap
通过给定回调函数同时执行过滤和映射,返回结果切片。
matching := lo.FilterMap([]string{"cpu", "gpu", "mouse", "keyboard"},
func(x string, _ int) (string, bool) {
if strings.HasSuffix(x, "pu") {
return "xpu", true
}
return "", false
})
// []string{"xpu", "xpu"}
func FilterMap[T any, R any](collection []T, callback func(item T, index int) (R, bool)) []RReduce
通过累加器函数将集合归约为单个值。
sum := lo.Reduce([]int{1, 2, 3, 4}, func(agg int, item int, _ int) int {
return agg + item
}, 0) // 10
func Reduce[T any, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) RForEach
遍历集合中的元素,并对每个元素调用回调函数。
lo.ForEach([]string{"hello", "world"}, func(x string, _ int) {
println(x)
})
func ForEach[T any](collection []T, callback func(item T, index int))Uniq
返回去重后的切片副本,仅保留每个值的首次出现。
lo.Uniq([]int{1, 2, 2, 1}) // []int{1, 2}
func Uniq[T comparable, Slice ~[]T](collection Slice) SliceGroupBy
根据从每个元素计算出的键对元素进行分组。
groups := lo.GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
return i % 3
})
// map[int][]int{0: {0, 3}, 1: {1, 4}, 2: {2, 5}}
func GroupBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) map[U]SliceChunk
将切片按给定大小拆分为多个块。
lo.Chunk([]int{0, 1, 2, 3, 4, 5}, 2)
// [][]int{{0, 1}, {2, 3}, {4, 5}}
func Chunk[T any, Slice ~[]T](collection Slice, size int) []SliceDrop
从切片开头丢弃 n 个元素。
lo.Drop([]int{0, 1, 2, 3, 4, 5}, 2) // []int{2, 3, 4, 5}
func Drop[T any, Slice ~[]T](collection Slice, n int) SliceTake
从切片开头取出前 n 个元素。
lo.Take([]int{0, 1, 2, 3, 4, 5}, 3) // []int{0, 1, 2}
func Take[T any, Slice ~[]T](collection Slice, n int) SliceReverse
原地反转切片并返回它。已弃用:请使用 mutable.Reverse。
lo.Reverse([]int{0, 1, 2, 3, 4, 5}) // []int{5, 4, 3, 2, 1, 0}
func Reverse[T any, Slice ~[]T](collection Slice) SliceConcat
返回包含所有集合中全部元素的新切片。
list1 := []int{0, 1}
list2 := []int{2, 3, 4, 5}
combined := lo.Concat(list1, list2) // []int{0, 1, 2, 3, 4, 5}
func Concat[T any, Slice ~[]T](collections ...Slice) SliceFlatten
将切片的切片展平一层。
flat := lo.Flatten([][]int{{0, 1}, {2, 3, 4, 5}})
// []int{0, 1, 2, 3, 4, 5}
func Flatten[T any, Slice ~[]T](collection []Slice) SliceRepeat
构建一个包含 N 个初始值副本的切片。
lo.Repeat(5, "42")
// []string{"42", "42", "42", "42", "42"}
func Repeat[T any](count int, initial T) []TKeyBy
使用回调函数计算键,将切片转换为映射。
m := lo.KeyBy([]string{"a", "aa", "aaa"}, func(str string) int {
return len(str)
})
// map[int]string{1: "a", 2: "aa", 3: "aaa"}
func KeyBy[K comparable, V any](collection []V, iteratee func(item V) K) map[K]VReject
返回使谓词函数返回 false 的元素(与 Filter 相反)。
lo.Reject([]int{1, 2, 3, 4}, func(x int, _ int) bool {
return x%2 == 0
}) // []int{1, 3}
func Reject[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) SliceCompact
返回所有非零值元素组成的切片。
lo.Compact([]string{"", "foo", "", "bar", ""})
// []string{"foo", "bar"}
func Compact[T comparable, Slice ~[]T](collection Slice) Slice映射操作
Keys
提取一个或多个映射的键到切片中。
keys := lo.Keys(map[string]int{"foo": 1, "bar": 2})
// []string{"foo", "bar"}
func Keys[K comparable, V any](in ...map[K]V) []KUniqKeys
跨多个映射生成去重后的键切片。
keys := lo.UniqKeys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 3})
// []string{"foo", "bar"}
func UniqKeys[K comparable, V any](in ...map[K]V) []KHasKey
判断映射中是否存在指定的键。
exists := lo.HasKey(map[string]int{"foo": 1}, "foo") // true
func HasKey[K comparable, V any](in map[K]V, key K) boolValues
提取一个或多个映射的值到切片中。
values := lo.Values(map[string]int{"foo": 1, "bar": 2}) // []int{1, 2}
func Values[K comparable, V any](in ...map[K]V) []VUniqValues
跨一个或多个映射提取去重后的值切片。
values := lo.UniqValues(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 2})
// []int{1, 2}
func UniqValues[K comparable, V comparable](in ...map[K]V) []VValueOr
返回指定键对应的值,若键不存在则返回回退值。
value := lo.ValueOr(map[string]int{"foo": 1}, "baz", 42) // 42
func ValueOr[K comparable, V any](in map[K]V, key K, fallback V) VPickBy
返回根据键/值谓词过滤后的同类型映射。
m := lo.PickBy(map[string]int{"foo": 1, "bar": 2, "baz": 3},
func(key string, value int) bool { return value%2 == 1 })
// map[string]int{"foo": 1, "baz": 3}
func PickBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) bool) MapPickByKeys
返回仅保留指定键的同类型映射。
m := lo.PickByKeys(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []string{"foo", "baz"})
// map[string]int{"foo": 1, "baz": 3}
func PickByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) MapPickByValues
返回仅保留值在给定值列表中的条目(同类型映射)。
m := lo.PickByValues(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []int{1, 3})
// map[string]int{"foo": 1, "baz": 3}
func PickByValues[K comparable, V comparable, Map ~map[K]V](in Map, values []V) MapOmitBy
返回排除掉匹配谓词的条目后的同类型映射。
m := lo.OmitBy(map[string]int{"foo": 1, "bar": 2, "baz": 3},
func(key string, value int) bool { return value%2 == 1 })
// map[string]int{"bar": 2}
func OmitBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) bool) MapOmitByKeys
返回移除指定键后的映射(PickByKeys 的反向操作)。
m := lo.OmitByKeys(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []string{"foo", "baz"})
// map[string]int{"bar": 2}
func OmitByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) MapOmitByValues
返回移除值在给定值列表中的条目后的映射(PickByValues 的反向操作)。
m := lo.OmitByValues(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []int{1, 3})
// map[string]int{"bar": 2}
func OmitByValues[K comparable, V comparable, Map ~map[K]V](in Map, values []V) MapEntries / ToPairs
将映射转换为键值对(Entry)切片。ToPairs 是它的别名。
entries := lo.Entries(map[string]int{"foo": 1, "bar": 2})
// []lo.Entry[string, int]{
// {Key: "foo", Value: 1},
// {Key: "bar", Value: 2},
// }
func Entries[K comparable, V any](in map[K]V) []Entry[K, V]
func ToPairs[K comparable, V any](in map[K]V) []Entry[K, V] // 别名FromEntries / FromPairs
从键值对(Entry)切片重建映射。FromPairs 是它的别名。
m := lo.FromEntries([]lo.Entry[string, int]{
{Key: "foo", Value: 1},
{Key: "bar", Value: 2},
})
// map[string]int{"foo": 1, "bar": 2}
func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V
func FromPairs[K comparable, V any](entries []Entry[K, V]) map[K]V // 别名Invert
交换映射的键和值(若有重复值,后出现的会覆盖先前的)。
result := lo.Invert(map[string]int{"a": 1, "b": 2})
// map[int]string{1: "a", 2: "b"}
func Invert[K comparable, V comparable](in map[K]V) map[V]KAssign
从左到右合并多个映射,后面的值会覆盖前面的键。
merged := lo.Assign(map[string]int{"a": 1, "b": 2}, map[string]int{"b": 3, "c": 4})
// map[string]int{"a": 1, "b": 3, "c": 4}
func Assign[K comparable, V any, Map ~map[K]V](maps ...Map) MapMapKeys
使用迭代函数转换映射的所有键,同时保留值。
in := map[int]int{1: 1, 2: 2}
out := lo.MapKeys(in, func(v int, k int) string {
return strconv.Itoa(k)
})
// map[string]int{"1": 1, "2": 2}
func MapKeys[K comparable, V any, R comparable](in map[K]V, iteratee func(value V, key K) R) map[R]VMapValues
使用迭代函数转换映射的所有值,同时保留键。
in := map[int]int64{1: 1, 2: 2}
out := lo.MapValues(in, func(v int64, k int) string {
return strconv.FormatInt(v, 10)
})
// map[int]string{1: "1", 2: "2"}
func MapValues[K comparable, V any, R any](in map[K]V, iteratee func(value V, key K) R) map[K]RMapEntries
使用迭代函数同时转换映射的键和值。
in := map[string]int{"foo": 1, "bar": 2}
out := lo.MapEntries(in, func(k string, v int) (int, string) {
return v, k
})
// map[int]string{1: "foo", 2: "bar"}
func MapEntries[K1 comparable, V1 any, K2 comparable, V2 any](in map[K1]V1, iteratee func(key K1, value V1) (K2, V2)) map[K2]V2MapToSlice
使用转换函数将映射的每个条目转换为切片中的一个元素。
m := map[int]int64{1: 4, 2: 5, 3: 6}
s := lo.MapToSlice(m, func(k int, v int64) string {
return fmt.Sprintf("%d_%d", k, v)
})
// []string{"1_4", "2_5", "3_6"}
func MapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(key K, value V) R) []RChunkEntries
将映射拆分为多个指定最大尺寸的较小映射。
chunks := lo.ChunkEntries(
map[string]int{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5},
3,
)
// []map[string]int{
// {"a": 1, "b": 2, "c": 3},
// {"d": 4, "e": 5},
// }
func ChunkEntries[K comparable, V any](m map[K]V, size int) []map[K]V说明:上述许多函数还有带
Err后缀的变体(如PickByErr、OmitByErr、MapKeysErr、MapValuesErr、MapEntriesErr、MapToSliceErr),其迭代/谓词函数可额外返回error,一旦出错立即中断并返回该错误。例如:out, err := lo.MapValuesErr(map[int]int64{1: 1, 2: 2}, func(v int64, _ int) (string, error) { if v == 2 { return "", fmt.Errorf("even not allowed") } return strconv.FormatInt(v, 10), nil }) // nil, error("even not allowed")
通道操作
ChannelDispatcher
根据指定策略将消息流分发到多个通道。
stream := make(chan int, 100)
for i := 0; i < 100; i++ {
stream <- i
}
close(stream)
channels := lo.ChannelDispatcher(stream, 3, 10, lo.DispatchingStrategyRoundRobin[int])
// 返回 3 个通道,采用轮询分发
func ChannelDispatcher[T any](stream <-chan T, count, channelBufferCap int, strategy DispatchingStrategy[T]) []<-chan TSliceToChannel
将切片转换为指定缓冲大小的通道。
items := []int{1, 2, 3, 4, 5}
ch := lo.SliceToChannel(10, items)
for item := range ch {
fmt.Println(item)
}
func SliceToChannel[T any](bufferSize int, collection []T) <-chan TChannelToSlice
将通道转换为切片。
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch)
slice := lo.ChannelToSlice(ch)
// slice 包含 [1, 2, 3]
func ChannelToSlice[T any](ch <-chan T) []TGenerator
从生成器回调函数创建通道。
gen := lo.Generator(10, func(yield func(int)) {
for i := 0; i < 10; i++ {
yield(i * 2)
}
})
for item := range gen {
fmt.Println(item)
}
// 打印偶数 0, 2, 4, 6, 8, ...
func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan TFanIn
将多个上游通道合并为单个下游通道。
merged := lo.FanIn(10, ch1, ch2)
// 顺序可能不固定
func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan TFanOut
将单个通道拆分为多个通道。
downstreams := lo.FanOut(3, 10, upstream)
// 返回 3 个通道
func FanOut[T any](count, channelsBufferCap int, upstream <-chan T) []<-chan TBuffer
从通道读取最多指定数量的元素,返回切片。
items, length, readTime, ok := lo.Buffer(ch, 3)
// items: []int{1, 2, 3}; length: 3; ok: true(通道已关闭)
func Buffer[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool)BufferWithContext
从通道读取元素,支持 context 取消。
items, length, readTime, ok := lo.BufferWithContext(ctx, ch, 5)
func BufferWithContext[T any](ctx context.Context, ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool)BufferWithTimeout
从通道读取元素,带超时限制。
items, length, readTime, ok := lo.BufferWithTimeout(ch, 5, 100*time.Millisecond)
// 超时则返回空切片
func BufferWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool)DispatchingStrategy
消息分发策略,作为 ChannelDispatcher 的最后一个参数传入,决定每条消息进入哪个通道。共六种内置变体:
- RoundRobin(轮询):依次循环分发到各通道。
- Random(随机):随机选择一个通道。
- WeightedRandom(加权随机):按给定权重随机选择。
- First(首个):分发到第一个未满的通道。
- Least(最少):分发到当前积压最少的通道。
- Most(最多):分发到当前积压最多的通道。
// 轮询分发到 3 个通道
channels := lo.ChannelDispatcher(stream, 3, 10, lo.DispatchingStrategyRoundRobin[int])
// 加权随机:第 3 个通道获得消息的概率最高
strategy := lo.DispatchingStrategyWeightedRandom[int]([]int{1, 3, 6})
channels = lo.ChannelDispatcher(stream, 3, 10, strategy)
func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan T) int
func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T) int
func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy[T]
func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) int
func DispatchingStrategyLeast[T any](msg T, index uint64, channels []<-chan T) int
func DispatchingStrategyMost[T any](msg T, index uint64, channels []<-chan T) int查找操作
IndexOf
返回值在切片中首次出现的索引,未找到则返回 -1。
idx := lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 2) // 2
idx = lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 6) // -1
func IndexOf[T comparable](collection []T, element T) intLastIndexOf
返回值在切片中最后出现的索引,未找到则返回 -1。
idx := lo.LastIndexOf([]int{0, 1, 2, 1, 2, 3}, 2) // 4
func LastIndexOf[T comparable](collection []T, element T) intHasPrefix
如果集合以给定前缀切片开头则返回 true。
lo.HasPrefix([]int{1, 2, 3, 4}, []int{1, 2}) // true
func HasPrefix[T comparable](collection []T, prefix []T) boolHasSuffix
如果集合以给定后缀切片结尾则返回 true。
lo.HasSuffix([]int{1, 2, 3, 4}, []int{3, 4}) // true
func HasSuffix[T comparable](collection []T, suffix []T) boolFind
根据谓词在切片中搜索元素,返回元素及表示是否成功的布尔值。
value, ok := lo.Find([]string{"a", "b", "c", "d"}, func(i string) bool {
return i == "b"
}) // "b", true
func Find[T any](collection []T, predicate func(item T) bool) (T, bool)FindErr
根据可返回错误的谓词搜索元素。
result, err := lo.FindErr([]string{"a", "b", "c", "d"}, func(i string) (bool, error) {
return i == "b", nil
}) // "b", nil
func FindErr[T any](collection []T, predicate func(item T) (bool, error)) (T, error)FindIndexOf
根据谓词搜索元素,返回元素、索引和布尔值。
val, idx, ok := lo.FindIndexOf([]string{"a", "b", "a", "b"}, func(i string) bool {
return i == "b"
}) // "b", 1, true
func FindIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool)FindLastIndexOf
搜索匹配谓词的最后一个元素,返回元素、索引和布尔值。
val, idx, ok := lo.FindLastIndexOf([]string{"a", "b", "a", "b"}, func(i string) bool {
return i == "b"
}) // "b", 3, true
func FindLastIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool)FindOrElse
根据谓词搜索元素,找到则返回该元素,否则返回回退值。
value := lo.FindOrElse([]string{"a", "b", "c", "d"}, "x", func(i string) bool {
return i == "b"
}) // "b"
func FindOrElse[T any](collection []T, fallback T, predicate func(item T) bool) TFindKey
返回第一个值等于给定值的键。
k, ok := lo.FindKey(map[string]int{"foo":1, "bar":2, "baz":3}, 2) // "bar", true
func FindKey[K comparable, V comparable](object map[K]V, value V) (K, bool)FindKeyBy
返回映射中第一个使谓词返回 true 的键。
k, ok := lo.FindKeyBy(map[string]int{"foo":1, "bar":2, "baz":3}, func(k string, v int) bool {
return k == "foo"
}) // "foo", true
func FindKeyBy[K comparable, V any](object map[K]V, predicate func(key K, value V) bool) (K, bool)FindUniques
返回集合中只出现一次的元素切片(保留原始顺序)。
lo.FindUniques([]int{1, 2, 2, 1, 2, 3}) // []int{3}
func FindUniques[T comparable, Slice ~[]T](collection Slice) SliceFindUniquesBy
返回按计算键唯一的元素切片。
lo.FindUniquesBy([]int{3, 4, 5, 6, 7}, func(i int) int {
return i % 3
}) // []int{5}
func FindUniquesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) SliceFindDuplicates
返回集合中每个重复元素首次出现的切片(保留顺序)。
lo.FindDuplicates([]int{1, 2, 2, 1, 2, 3}) // []int{1, 2}
func FindDuplicates[T comparable, Slice ~[]T](collection Slice) SliceFindDuplicatesBy
返回按键计算的每个重复元素首次出现的切片。
lo.FindDuplicatesBy([]int{3, 4, 5, 6, 7}, func(i int) int {
return i % 3
}) // []int{3, 4}
func FindDuplicatesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) SliceFindDuplicatesByErr
按键返回重复元素切片,支持错误处理。
result, err := lo.FindDuplicatesByErr([]int{3, 4, 5, 6, 7}, func(i int) (int, error) {
return i % 3, nil
}) // []int{3, 4}, <nil>
func FindDuplicatesByErr[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) (U, error)) (Slice, error)Min
返回集合的最小值。
lo.Min([]int{1, 2, 3}) // 1
func Min[T constraints.Ordered](collection []T) TMinIndex
返回最小值及其索引。
value, idx := lo.MinIndex([]int{2, 5, 3}) // value == 2, idx == 0
func MinIndex[T constraints.Ordered](collection []T) (T, int)MinBy
使用给定比较函数搜索集合的最小值。
type Point struct{ X int }
min := lo.MinBy([]Point{{1}, {5}, {3}}, func(a, b Point) bool {
return a.X < b.X
}) // {1}
func MinBy[T any](collection []T, comparison func(a T, b T) bool) TMinByErr
使用带错误处理的比较函数搜索集合的最小值;比较函数返回错误时立即中断。
type Point struct{ X int }
min, err := lo.MinByErr([]Point{{1}, {5}, {3}}, func(a, b Point) (bool, error) {
return a.X < b.X, nil
}) // {1}, <nil>
func MinByErr[T any](collection []T, comparison func(a T, b T) (bool, error)) (T, error)MinIndexBy
使用比较函数搜索最小值,返回值及其索引。
type Point struct{ X int }
value, idx := lo.MinIndexBy([]Point{{1}, {5}, {3}}, func(a, b Point) bool {
return a.X < b.X
}) // value == {1}, idx == 0
func MinIndexBy[T any](collection []T, comparison func(a T, b T) bool) (T, int)MinIndexByErr
使用带错误处理的比较函数搜索最小值,返回值、索引和错误。
type Point struct{ X int }
val, idx, err := lo.MinIndexByErr([]Point{{1}, {5}, {3}}, func(a, b Point) (bool, error) {
return a.X < b.X, nil
}) // val == {1}, idx == 0, err == nil
func MinIndexByErr[T any](collection []T, comparison func(a T, b T) (bool, error)) (T, int, error)Max
搜索集合的最大值。
max := lo.Max([]int{2, 5, 3}) // 5
func Max[T constraints.Ordered](collection []T) TMaxIndex
返回最大值及其索引。
value, idx := lo.MaxIndex([]int{2, 5, 3}) // value == 5, idx == 1
func MaxIndex[T constraints.Ordered](collection []T) (T, int)MaxBy
使用给定比较函数搜索集合的最大值。
type Point struct{ X int }
max := lo.MaxBy([]Point{{1}, {5}, {3}}, func(a, b Point) bool {
return a.X > b.X
}) // {5}
func MaxBy[T any](collection []T, comparison func(a T, b T) bool) TMaxByErr
使用带错误处理的比较函数搜索集合的最大值;比较函数返回错误时立即中断。
type Point struct{ X int }
max, err := lo.MaxByErr([]Point{{1}, {5}, {3}}, func(a, b Point) (bool, error) {
return a.X > b.X, nil
}) // {5}, <nil>
func MaxByErr[T any](collection []T, comparison func(a T, b T) (bool, error)) (T, error)MaxIndexBy
使用给定比较函数返回最大值及其索引。
type Point struct{ X int }
value, idx := lo.MaxIndexBy([]Point{{1}, {5}, {3}}, func(a, b Point) bool {
return a.X > b.X
}) // value == {5}, idx == 1
func MaxIndexBy[T any](collection []T, comparison func(a T, b T) bool) (T, int)MaxIndexByErr
带错误处理返回最大值及其索引。
type Point struct{ X int }
val, idx, err := lo.MaxIndexByErr([]Point{{1}, {5}, {3}}, func(a, b Point) (bool, error) {
return a.X > b.X, nil
}) // val == {5}, idx == 1, err == nil
func MaxIndexByErr[T any](collection []T, comparison func(a T, b T) (bool, error)) (T, int, error)Earliest
搜索提供参数中最早的 time.Time。
t1 := time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
t2 := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
min := lo.Earliest(t2, t1) // 2023-01-01 00:00:00 +0000 UTC
func Earliest(times ...time.Time) time.TimeEarliestBy
在集合中搜索由谓词提取出最早时间的元素。
type Event struct{ At time.Time }
first := lo.EarliestBy(events, func(e Event) time.Time {
return e.At
})
func EarliestBy[T any](collection []T, iteratee func(item T) time.Time) TEarliestByErr
在集合中搜索由迭代函数提取出最早时间的元素,支持错误处理。
type Event struct{ At time.Time }
events := []Event{{At: time.Now().Add(2 * time.Hour)}, {At: time.Now()}}
first, err := lo.EarliestByErr(events, func(e Event) (time.Time, error) {
return e.At, nil
}) // 时间最早的 Event, <nil>
func EarliestByErr[T any](collection []T, iteratee func(item T) (time.Time, error)) (T, error)Latest
搜索提供参数中最晚的 time.Time。
max := lo.Latest(t1, t2) // 2024-01-01 00:00:00 +0000 UTC
func Latest(times ...time.Time) time.TimeLatestBy
在集合中搜索由谓词提取出最晚时间的元素。
last := lo.LatestBy(events, func(e Event) time.Time {
return e.At
})
func LatestBy[T any](collection []T, iteratee func(item T) time.Time) TLatestByErr
在集合中搜索由迭代函数提取出最晚时间的元素,支持错误处理。
type Event struct{ At time.Time }
events := []Event{{At: time.Now()}, {At: time.Now().Add(2 * time.Hour)}}
last, err := lo.LatestByErr(events, func(e Event) (time.Time, error) {
return e.At, nil
}) // 时间最晚的 Event, <nil>
func LatestByErr[T any](collection []T, iteratee func(item T) (time.Time, error)) (T, error)First
返回集合的第一个元素及其是否存在。
v, ok := lo.First([]int{1, 2, 3}) // v == 1, ok == true
func First[T any](collection []T) (T, bool)FirstOrEmpty
返回集合的第一个元素,若为空则返回零值。
v := lo.FirstOrEmpty([]int{}) // v == 0
func FirstOrEmpty[T any](collection []T) TFirstOr
返回集合的第一个元素,若为空则返回回退值。
v := lo.FirstOr([]int{}, -1) // v == -1
func FirstOr[T any](collection []T, fallback T) TLast
返回集合的最后一个元素及其是否存在。
v, ok := lo.Last([]int{1, 2, 3}) // v == 3, ok == true
func Last[T any](collection []T) (T, bool)LastOrEmpty
返回集合的最后一个元素,若为空则返回零值。
v := lo.LastOrEmpty([]int{}) // v == 0
func LastOrEmpty[T any](collection []T) TLastOr
返回集合的最后一个元素,若为空则返回回退值。
v := lo.LastOr([]int{}, -1) // v == -1
func LastOr[T any](collection []T, fallback T) TNth
返回集合中索引为 nth 的元素,支持负索引。
v, _ := lo.Nth([]int{10, 20, 30}, 1) // v == 20
func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error)NthOr
返回集合中索引为 nth 的元素,越界则返回回退值。
v := lo.NthOr([]int{10, 20, 30}, 10, -1) // v == -1
func NthOr[T any, N constraints.Integer](collection []T, nth N, fallback T) TNthOrEmpty
返回集合中索引为 nth 的元素,越界则返回零值。
v := lo.NthOrEmpty([]int{10, 20, 30}, 10) // v == 0
func NthOrEmpty[T any, N constraints.Integer](collection []T, nth N) TSample
从集合中返回一个随机元素。
v := lo.Sample([]int{10, 20, 30})
func Sample[T any](collection []T) TSampleBy
使用提供的随机索引生成器从集合中返回一个随机元素。
v := lo.SampleBy([]int{10, 20, 30}, func(n int) int {
return 0
}) // v == 10
func SampleBy[T any](collection []T, randomIntGenerator func(int) int) TSamples
从集合中返回 N 个不重复的随机元素。
v := lo.Samples([]int{10, 20, 30}, 2)
func Samples[T any, Slice ~[]T](collection Slice, count int) SliceSamplesBy
使用提供的随机索引生成器从集合中返回 N 个不重复的随机元素。
v := lo.SamplesBy([]int{10, 20, 30}, 2, func(n int) int {
return 0
})
func SamplesBy[T any, Slice ~[]T](collection Slice, count int, randomIntGenerator func(int) int) Slice字符串操作
RandomString
使用给定字符集生成指定长度的随机文本。
lo.RandomString(5, lo.LettersCharset) // 返回随机的 5 个字符
func RandomString(size int, charset []rune) stringSubstring
从指定偏移量开始提取指定长度的文本片段,支持负索引。
lo.Substring("hello", 2, 3) // "llo"
func Substring[T ~string](str T, offset int, length uint) TChunkString
将文本拆分为等长的片段。
lo.ChunkString("1234567", 2) // []string{"12", "34", "56", "7"}
func ChunkString[T ~string](str T, size int) []TRuneLength
统计 Unicode 字符数量(而非字节数)。
lo.RuneLength("hellô") // 5
func RuneLength(str string) intPascalCase
转换为 PascalCase(首字母大写驼峰)格式。
lo.PascalCase("hello_world") // "HelloWorld"
func PascalCase(str string) stringCamelCase
转换为 camelCase(小驼峰)格式。
lo.CamelCase("hello_world") // "helloWorld"
func CamelCase(str string) stringKebabCase
转换为 kebab-case(连字符分隔)格式。
lo.KebabCase("helloWorld") // "hello-world"
func KebabCase(str string) stringSnakeCase
转换为 snake_case(下划线分隔)格式。
lo.SnakeCase("HelloWorld") // "hello_world"
func SnakeCase(str string) stringWords
将文本拆分为单词列表。
lo.Words("helloWorld") // []string{"hello", "world"}
func Words(str string) []stringCapitalize
将首字母大写,其余字母小写。
lo.Capitalize("heLLO") // "Hello"
func Capitalize(str string) stringEllipsis
将文本截断到指定长度并在需要时追加 "...",保留多字节字符。
lo.Ellipsis("Lorem Ipsum", 5) // "Lo..."
func Ellipsis(str string, length int) string函数操作
Partial / PartialX
预绑定函数的第一个参数。Partial1 到 Partial5 变体支持 2 到 6 个输入参数的函数。
add := func(x, y int) int { return x + y }
add10 := lo.Partial(add, 10)
sum := add10(5) // 15
func Partial[T1, T2, R any](f func(a T1, b T2) R, arg1 T1) func(T2) R
func Partial1[T1, T2, R any](f func(T1, T2) R, arg1 T1) func(T2) R
func Partial2[T1, T2, T3, R any](f func(T1, T2, T3) R, arg1 T1) func(T2, T3) R
// ... Partial3 到 Partial5 变体类似条件操作
Ternary
单行 if/else:条件为 true 时返回 ifOutput,否则返回 elseOutput。
result := lo.Ternary(true, "a", "b") // "a"
age := 25
status := lo.Ternary(age >= 18, "adult", "minor") // "adult"
func Ternary[T any](condition bool, ifOutput T, elseOutput T) TTernaryF
函数变体,仅惰性求值所需分支,避免不必要的计算或 nil 解引用。
str := lo.TernaryF(s != nil, func() string {
return *s
}, func() string {
return "default value"
})
func TernaryF[T any](condition bool, ifFunc func() T, elseFunc func() T) TIf / ElseIf / Else
流式条件构建器,允许链式调用 If/ElseIf/Else 条件。
result := lo.If(false, 1).ElseIf(true, 2).Else(3) // 2
func If[T any](condition bool, result T) *ifElse[T]
func (i *ifElse[T]) ElseIf(condition bool, result T) *ifElse[T]
func (i *ifElse[T]) Else(result T) TSwitch / Case / Default
使用谓词值发起函数式 switch/case/default 链。
result := lo.Switch(2).Case(1, "1").Case(2, "2").Default("3") // "2"
func Switch[T comparable, R any](predicate T) *switchCase[T, R]
func (s *switchCase[T, R]) Case(val T, result R) *switchCase[T, R]
func (s *switchCase[T, R]) Default(result R) R数学操作
Range
创建从 0 开始的整数切片;负长度产生递减序列。
lo.Range(4) // []int{0, 1, 2, 3}
func Range(elementNum int) []intRangeFrom
从指定起始值开始,按定义长度生成序列。
lo.RangeFrom(1, 5) // []int{1, 2, 3, 4, 5}
func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum int) []TRangeWithSteps
从 start 到 end(不含)按 step 步进生成序列;step 为 0 时返回空。
lo.RangeWithSteps(0, 20, 5) // []int{0, 5, 10, 15}
func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step T) []TClamp
将值约束在指定的闭区间边界内。
lo.Clamp(42, -10, 10) // 10
func Clamp[T constraints.Ordered](value T, min T, max T) TSum
聚合集合中的值;空集合返回 0。
lo.Sum([]int{1, 2, 3, 4, 5}) // 15
func Sum[T constraints.Float | constraints.Integer | constraints.Complex](collection []T) TSumBy
通过谓词函数计算总和。
lo.SumBy([]string{"foo", "bar"}, func(item string) int { return len(item) }) // 6
func SumBy[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection []T, iteratee func(item T) R) RProduct
将集合元素相乘;空集合默认返回 1。
lo.Product([]int{1, 2, 3, 4, 5}) // 120
func Product[T constraints.Float | constraints.Integer | constraints.Complex](collection []T) TProductBy
通过迭代函数将每个元素映射为数值后计算乘积。
lo.ProductBy([]string{"foo", "bar"}, func(item string) int {
return len(item)
}) // 9 (3 × 3)
func ProductBy[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection []T, iteratee func(item T) R) RMean
计算算术平均值;空集合返回 0。
lo.Mean([]int{2, 3, 4, 5}) // 3
func Mean[T constraints.Float | constraints.Integer](collection []T) TMeanBy
通过迭代函数将每个元素映射为数值后计算算术平均值。
list := []string{"aa", "bbb", "cccc", "ddddd"}
lo.MeanBy(list, func(item string) float64 {
return float64(len(item))
}) // 3.5 ((2+3+4+5)/4)
func MeanBy[T any, R constraints.Float | constraints.Integer](collection []T, iteratee func(item T) R) RMode
找出出现频率最高的值;返回所有并列的值。
lo.Mode([]int{2, 2, 3, 3}) // []int{2, 3}
func Mode[T constraints.Integer | constraints.Float](collection []T) []T交集操作
Contains
判断元素是否存在于集合中。
present := lo.Contains([]int{0, 1, 2, 3, 4, 5}, 5) // true
func Contains[T comparable](collection []T, element T) boolContainsBy
判断谓词是否对集合中任一元素返回 true。
exists := lo.ContainsBy([]int{0, 1, 2, 3}, func(x int) bool { return x == 3 }) // true
func ContainsBy[T any](collection []T, predicate func(item T) bool) boolEvery
验证子集的所有元素是否都包含在集合中。
ok := lo.Every([]int{0, 1, 2, 3, 4, 5}, []int{0, 2}) // true
func Every[T comparable](collection []T, subset []T) boolEveryBy
如果谓词对所有元素都成立(或集合为空)则返回 true。
ok := lo.EveryBy([]int{1, 2, 3, 4}, func(x int) bool { return x < 5 }) // true
func EveryBy[T any](collection []T, predicate func(item T) bool) boolSome
判断子集中是否至少有一个元素存在于集合中。
ok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{0, 6}) // true
func Some[T comparable](collection []T, subset []T) boolSomeBy
如果谓词匹配任一元素则返回 true。
ok := lo.SomeBy([]int{1, 2, 3, 4}, func(x int) bool { return x < 3 }) // true
func SomeBy[T any](collection []T, predicate func(item T) bool) boolNone
确认子集中没有元素出现在集合中。
ok := lo.None([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6}) // true
func None[T comparable](collection []T, subset []T) boolNoneBy
如果谓词对所有元素都不成立(或集合为空)则返回 true。
ok := lo.NoneBy([]int{1, 2, 3, 4}, func(x int) bool { return x < 0 }) // true
func NoneBy[T any](collection []T, predicate func(item T) bool) boolIntersect
计算多个集合之间的交集。
lo.Intersect([]int{0, 3, 5, 7}, []int{3, 5}, []int{0, 1, 2, 0, 3, 0}) // []int{3}
func Intersect[T comparable, Slice ~[]T](lists ...Slice) SliceIntersectBy
使用自定义键转换函数计算多个集合的交集(按转换后的键比较)。
transform := func(v int) string { return strconv.Itoa(v) }
lo.IntersectBy(transform, []int{0, 3, 5, 7}, []int{3, 5}, []int{0, 1, 2, 0, 3, 0})
// []int{3}
func IntersectBy[T any, K comparable, Slice ~[]T](transform func(T) K, lists ...Slice) SliceDifference
返回两个集合各自独有的元素(分别返回)。
left, right := lo.Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 2, 6})
// ([]int{1, 3, 4, 5}, []int{6})
func Difference[T comparable, Slice ~[]T](list1 Slice, list2 Slice) (Slice, Slice)Union
合并所有集合中的不重复元素,保留顺序。
lo.Union([]int{0, 1, 2, 3, 4, 5}, []int{0, 2}, []int{0, 10})
// []int{0, 1, 2, 3, 4, 5, 10}
func Union[T comparable, Slice ~[]T](lists ...Slice) SliceUnionBy
基于键函数合并集合中的不重复元素(按计算键去重),保留首次出现的顺序。
lo.UnionBy(func(i int) int { return i / 2 }, []int{0, 1, 2, 3, 4, 5}, []int{0, 2, 10})
// []int{0, 2, 4, 10}
func UnionBy[T any, V comparable, Slice ~[]T](iteratee func(item T) V, lists ...Slice) SliceUnionByErr
与 UnionBy 类似,但键函数可返回错误,遇错即停。
result, err := lo.UnionByErr(
func(i int) (int, error) { return i / 2, nil },
[]int{0, 1, 2, 3, 4, 5}, []int{0, 2, 10},
) // []int{0, 2, 4, 10}, nil
func UnionByErr[T any, V comparable, Slice ~[]T](iteratee func(item T) (V, error), lists ...Slice) (Slice, error)Without
从集合中排除指定的值。
lo.Without([]int{0, 2, 10}, 2) // []int{0, 10}
func Without[T comparable, Slice ~[]T](collection Slice, exclude ...T) SliceWithoutBy
从集合中排除「提取键」命中排除列表的元素,适用于结构体切片按某字段过滤。
type User struct {
ID int
Name string
}
users := []User{{1, "Alice"}, {2, "Bob"}, {3, "Charlie"}}
lo.WithoutBy(users, func(u User) int { return u.ID }, 2, 3)
// []User{{1, "Alice"}}
func WithoutBy[T any, K comparable, Slice ~[]T](collection Slice, iteratee func(item T) K, exclude ...K) SliceWithoutByErr
按键函数排除元素,支持错误处理;键函数返回错误时立即中断。
type User struct {
ID int
Name string
}
users := []User{{1, "Alice"}, {2, "Bob"}, {3, "Charlie"}}
filtered, err := lo.WithoutByErr(users, func(u User) (int, error) {
return u.ID, nil
}, 2, 3) // []User{{1, "Alice"}}, nil
func WithoutByErr[T any, K comparable, Slice ~[]T](collection Slice, iteratee func(item T) (K, error), exclude ...K) (Slice, error)WithoutEmpty
移除零值;已弃用,推荐使用 Compact。
lo.WithoutEmpty([]int{0, 2, 10}) // []int{2, 10}
func WithoutEmpty[T comparable, Slice ~[]T](collection Slice) SliceWithoutNth
排除指定索引处的元素。
lo.WithoutNth([]int{-2, -1, 0, 1, 2}, 3) // []int{-2, -1, 0, 2}
func WithoutNth[T comparable, Slice ~[]T](collection Slice, nths ...int) SliceElementsMatch
如果两个列表包含相同的元素集合(含相同重复次数,忽略顺序)则返回 true。
lo.ElementsMatch([]int{1, 1, 2}, []int{2, 1, 1}) // true
func ElementsMatch[T comparable, Slice ~[]T](list1 Slice, list2 Slice) boolElementsMatchBy
使用计算键而非直接元素比较两个列表是否包含相同的元素集合(含相同重复次数,忽略顺序)。
type Item struct{ ID string }
lo.ElementsMatchBy(
[]Item{{"a"}, {"b"}},
[]Item{{"b"}, {"a"}},
func(i Item) string { return i.ID },
) // true
func ElementsMatchBy[T any, K comparable](list1 []T, list2 []T, iteratee func(item T) K) bool重试操作
Attempt
执行函数最多 N 次直到成功。返回迭代次数(从 1 开始计数)和最终错误。
iter, err := lo.Attempt(3, func(index int) error {
// 第三次尝试成功
return nil
}) // iter: 3, err: nil
func Attempt(maxIteration int, f func(index int) error) (int, error)AttemptWithDelay
与 Attempt 类似,但在每次调用之间插入固定延迟。返回迭代次数、总耗时和最终错误。
iter, dur, err := lo.AttemptWithDelay(3, 100*time.Millisecond,
func(i int, d time.Duration) error {
if i == 1 {
return nil // 第二次尝试成功
}
return errors.New("失败")
},
) // iter: 2, dur: ~100ms, err: nil
func AttemptWithDelay(maxIteration int, delay time.Duration,
f func(index int, duration time.Duration) error) (int, time.Duration, error)AttemptWhile
重试直到返回 nil 或达到最大迭代次数。回调的第二个返回值(bool)决定是否继续重试:返回 false 则立即停止。
count, err := lo.AttemptWhile(5, func(i int) (error, bool) {
if i == 2 {
return nil, false // 第三次成功并停止
}
return errors.New("失败"), true // 继续重试
}) // count: 3, err: nil
func AttemptWhile(maxIteration int, f func(int) (error, bool)) (int, error)AttemptWhileWithDelay
结合 AttemptWhile 与延迟行为,在每次重试间插入延迟并跟踪总耗时。
count, dur, err := lo.AttemptWhileWithDelay(5, time.Millisecond,
func(i int, d time.Duration) (error, bool) {
return errors.New("错误"), i < 1 // 仅重试一次后停止
},
) // count: 2, dur: ~1ms, err: 错误
func AttemptWhileWithDelay(maxIteration int, delay time.Duration,
f func(int, time.Duration) (error, bool)) (int, time.Duration, error)并发操作
NewDebounce
创建防抖函数,将回调延迟到最后一次调用后经过等待时间才执行。返回防抖函数和取消函数。
debounce, cancel := lo.NewDebounce(
100*time.Millisecond,
func() {
println("防抖后只调用一次!")
},
)
for i := 0; i < 10; i++ {
debounce()
}
time.Sleep(200 * time.Millisecond)
cancel()
func NewDebounce(duration time.Duration, f ...func()) (func(), func())NewDebounceBy
按键创建防抖函数,为不同的键跟踪各自独立的计时器;回调可获知该键累计触发的次数 count。
debounce, cancel := lo.NewDebounceBy[string](
100*time.Millisecond,
func(key string, count int) {
println(key, "called", count, "times")
},
)
for i := 0; i < 5; i++ {
debounce("action")
}
time.Sleep(200 * time.Millisecond)
// 输出: action called 5 times
cancel("action")
func NewDebounceBy[T comparable](duration time.Duration, f ...func(key T, count int)) (func(key T), func(key T))Synchronize
用互斥锁包装回调以确保顺序执行,可接受可选的自定义 locker。
s := lo.Synchronize()
for i := 0; i < 10; i++ {
go s.Do(func() { println("sequential") })
}
func Synchronize(opt ...sync.Locker) *synchronizeAsync / AsyncX
异步运行函数并通过通道返回结果(Async0 到 Async6 支持 0-6 个返回值)。
ch := lo.Async(func() int {
time.Sleep(10 * time.Millisecond)
return 42
})
v := <-ch
func Async[A any](f func() A) <-chan A
func Async0(f func()) <-chan struct{}
func Async2[A, B any](f func() (A, B)) <-chan Tuple2[A, B]
// ... Async3 到 Async6 变体类似WaitFor
周期性运行直到条件验证通过,返回迭代次数、耗时和成功状态。包含 context 感知变体。
iterations, elapsed, ok := lo.WaitFor(
func(i int) bool {
return i > 5
},
10*time.Millisecond,
time.Millisecond,
)
func WaitFor(condition func(i int) bool, timeout time.Duration, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool)
func WaitForWithContext(ctx context.Context, condition func(ctx context.Context, i int) bool, timeout time.Duration, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool)NewTransaction
创建 Saga 模式事务,串联各步骤并附带回滚函数用于错误处理;失败时按相反顺序回滚。
type Acc struct{ Sum int }
tx := lo.NewTransaction[Acc]().
Then(
func(a Acc) (Acc, error) { a.Sum += 10; return a, nil },
func(a Acc) Acc { a.Sum -= 10; return a },
).
Then(
func(a Acc) (Acc, error) { a.Sum *= 3; return a, nil },
func(a Acc) Acc { a.Sum /= 3; return a },
)
res, err := tx.Process(Acc{Sum: 1})
// res.Sum == 33, err == nil
func NewTransaction[T any]() *Transaction[T]NewThrottle
创建节流函数,每个时间间隔内最多调用一次回调,并提供 reset 函数。
throttle, reset := lo.NewThrottle(
100*time.Millisecond,
func() {
println("tick")
},
)
func NewThrottle(interval time.Duration, f ...func()) (throttle func(), reset func())NewThrottleWithCount
创建节流函数,每个时间间隔内最多调用 count 次回调。
throttle, reset := lo.NewThrottleWithCount(
100*time.Millisecond,
2, // 每 100ms 内最多执行 2 次
func() { println("throttled") },
)
for i := 0; i < 6; i++ {
throttle()
time.Sleep(30 * time.Millisecond)
}
reset()
func NewThrottleWithCount(interval time.Duration, count int, f ...func()) (throttle func(), reset func())NewThrottleBy
按键创建节流函数,相同键在每个时间间隔内最多触发一次。
throttle, reset := lo.NewThrottleBy[string](
100*time.Millisecond,
func(key string) { println("key:", key) },
)
for i := 0; i < 5; i++ {
throttle("user123") // 同一键 "user123" 每 100ms 内只会触发一次
time.Sleep(25 * time.Millisecond)
}
reset()
func NewThrottleBy[T comparable](interval time.Duration, f ...func(key T)) (throttle func(key T), reset func())NewThrottleByWithCount
按键创建节流函数,相同键在每个时间间隔内最多触发 count 次。
throttle, reset := lo.NewThrottleByWithCount[string](
100*time.Millisecond,
3, // 同一键每 100ms 内最多执行 3 次
func(key string) { println("key:", key) },
)
for i := 0; i < 10; i++ {
throttle("event")
time.Sleep(20 * time.Millisecond)
}
reset()
func NewThrottleByWithCount[T comparable](interval time.Duration, count int, f ...func(key T)) (throttle func(key T), reset func())元组操作
T2-T9 (TupleX)
从 2 到 9 个元素创建元组值。
t := lo.T3(1, "a", true)
func T2[A, B any](a A, b B) Tuple2[A, B]
func T3[A, B, C any](a A, b B, c C) Tuple3[A, B, C]
// ... T4 到 T9 变体类似Unpack2-Unpack9 (UnpackX)
从元组中提取值到独立变量。
a, b, c := lo.Unpack3(lo.T3(1, "a", true))
func Unpack2[A, B any](tuple Tuple2[A, B]) (A, B)
// ... Unpack3 到 Unpack9 变体类似Zip2-Zip9 (ZipX)
将多个切片压缩为元组切片。
xs := []int{1, 2}
ys := []string{"a", "b"}
pairs := lo.Zip2(xs, ys)
func Zip2[A, B any](a []A, b []B) []Tuple2[A, B]
// ... Zip3 到 Zip9 变体类似Unzip2-Unzip9 (UnzipX)
将元组切片拆分回多个并行切片(Zip 的逆操作)。
pairs := []lo.Tuple2[int, string]{{A: 1, B: "a"}, {A: 2, B: "b"}}
xs, ys := lo.Unzip2(pairs)
func Unzip2[A, B any](tuples []Tuple2[A, B]) ([]A, []B)
// ... Unzip3 到 Unzip9 变体类似ZipBy2-ZipBy9 (ZipByX)
压缩多个切片,并对每组对应元素应用转换函数,直接得到结果切片。
out := lo.ZipBy2([]int{1, 2}, []string{"a", "b"}, func(a int, b string) string {
return fmt.Sprintf("%d%s", a, b)
})
// []string{"1a", "2b"}
func ZipBy2[A any, B any, Out any](a []A, b []B, predicate func(a A, b B) Out) []Out
// ... ZipBy3 到 ZipBy9 变体类似ZipByErr2-ZipByErr9 (ZipByErrX)
带错误处理的压缩,转换失败时立即停止迭代。
func ZipByErr2[A any, B any, Out any](a []A, b []B, iteratee func(a A, b B) (Out, error)) ([]Out, error)
// ... ZipByErr3 到 ZipByErr9 变体类似UnzipBy2-UnzipBy9 (UnzipByX)
将每个输入元素通过转换函数拆成多个值,再分别收集为并行切片。
names := []string{"Alice:30", "Bob:25"}
ns, ages := lo.UnzipBy2(names, func(s string) (string, int) {
parts := strings.Split(s, ":")
age, _ := strconv.Atoi(parts[1])
return parts[0], age
})
// ns: []string{"Alice", "Bob"}
// ages: []int{30, 25}
func UnzipBy2[In any, A any, B any](items []In, predicate func(In) (a A, b B)) ([]A, []B)
// ... UnzipBy3 到 UnzipBy9 变体类似UnzipByErr2-UnzipByErr9 (UnzipByErrX)
与 UnzipByX 类似,但支持错误处理。
func UnzipByErr2[In any, A any, B any](items []In, predicate func(In) (a A, b B, err error)) ([]A, []B, error)
// ... UnzipByErr3 到 UnzipByErr9 变体类似CrossJoin2-CrossJoin9 (CrossJoinX)
计算输入切片的笛卡尔积,返回所有可能的组合元组。
a := []int{1, 2}
b := []string{"x", "y"}
pairs := lo.CrossJoin2(a, b)
func CrossJoin2[A, B any](listA []A, listB []B) []Tuple2[A, B]
// ... CrossJoin3 到 CrossJoin9 变体类似CrossJoinBy2-CrossJoinBy9 (CrossJoinByX)
计算笛卡尔积,并对每个组合应用转换函数投影为结果。
out := lo.CrossJoinBy2([]int{1, 2}, []string{"a", "b"}, func(a int, b string) string {
return fmt.Sprintf("%d-%s", a, b)
})
// []string{"1-a", "1-b", "2-a", "2-b"}
func CrossJoinBy2[A, B, Out any](listA []A, listB []B, transform func(a A, b B) Out) []Out
// ... CrossJoinBy3 到 CrossJoinBy9 变体类似CrossJoinByErr2-CrossJoinByErr9 (CrossJoinByErrX)
带错误处理的笛卡尔积。
func CrossJoinByErr2[A any, B any, Out any](listA []A, listB []B, transform func(a A, b B) (Out, error)) ([]Out, error)
// ... CrossJoinByErr3 到 CrossJoinByErr9 变体类似时间操作
Duration / DurationX
测量函数的执行时间。各变体在返回耗时的同时返回函数的 0 到 10 个返回值。
// 基础变体 - 仅返回耗时
elapsedOnly := lo.Duration(func() {
time.Sleep(3 * time.Millisecond)
})
// 零返回值变体
elapsed := lo.Duration0(func() {
time.Sleep(10 * time.Millisecond)
})
// 一个返回值变体 - 捕获函数结果加耗时
v, dur := lo.Duration1(func() int {
time.Sleep(5 * time.Millisecond)
return 123
})
// 两个返回值变体
a, b, elapsed2 := lo.Duration2(func() (int, string) {
time.Sleep(2 * time.Millisecond)
return 7, "x"
})
func Duration(cb func()) time.Duration
func Duration0(cb func()) time.Duration
func Duration1[A any](cb func() A) (A, time.Duration)
func Duration2[A, B any](cb func() (A, B)) (A, B, time.Duration)
// ... 直到 Duration10错误处理
Validate
创建带格式化消息的条件错误。条件通过时返回 nil。
err := lo.Validate(len(slice) == 0, "Slice should be empty")
func Validate(ok bool, format string, args ...any) errorMust / MustX
如果发生错误则 panic,否则返回成功的值。支持 0-6 个返回值。
v := lo.Must(strconv.Atoi("10"))
a, b, c, d := lo.Must4(myFunc())
func Must[T any](val T, err error) T
func Must0(err error)
// ... Must1 到 Must6 变体类似Try / TryX
执行函数,发生错误/panic 时返回 false。涵盖 0-6 个返回值的回调。
ok := lo.Try(func() error { return fmt.Errorf("boom") })
func Try(callback func() error) bool
func Try0(callback func()) bool
// ... Try1 到 Try6 变体类似TryOr / TryOrX
与 Try 类似,但发生错误时返回回退值加成功标志。处理 1-6 个返回值。
value, ok := lo.TryOr(func() (int, error) { return 0, fmt.Errorf("boom") }, 123)
func TryOr[A any](callback func() (A, error), fallbackA A) (A, bool)
// ... TryOr1 到 TryOr6 变体类似TryWithErrorValue
执行函数并返回错误/panic 值及成功布尔值。
err, ok := lo.TryWithErrorValue(func() error { panic("error") })
func TryWithErrorValue(callback func() error) (errorValue any, ok bool)TryCatch
在回调发生错误/panic 时调用 catch 处理器。
lo.TryCatch(func() error { panic("error") }, func() { /* 处理 */ })
func TryCatch(callback func() error, catch func())TryCatchWithErrorValue
与 TryCatch 类似,但将捕获到的错误值/panic 值传递给处理器,便于做针对性处理。
lo.TryCatchWithErrorValue(
func() error {
panic("something went wrong!")
},
func(val any) {
fmt.Printf("捕获到: %v\n", val)
// 输出: 捕获到: something went wrong!
},
)
func TryCatchWithErrorValue(callback func() error, catch func(any))ErrorsAs
标准库 errors.As 的泛型封装,返回类型化错误和成功标志。
if rateLimitErr, ok := lo.ErrorsAs[*RateLimitError](err); ok {
// 处理
}
func ErrorsAs[T error](err error) (T, bool)Assert / Assertf
条件失败时 panic;条件为 true 时不做任何操作。支持可选消息和格式化。
lo.Assert(age >= 15, "user age must be >= 15")
func Assert(condition bool, message ...string)
func Assertf(condition bool, format string, args ...any)类型操作
Nil
返回指定类型的 nil 指针。
ptr := lo.Nil[string]() // (*string)(nil)
ptr2 := lo.Nil[int]() // (*int)(nil)
func Nil[T any]() *TIsNil
检查输入是否为 nil(适用于指针、接口、映射、切片、通道、函数)。
lo.IsNil(nil) // true
var ptr *int
lo.IsNil(ptr) // true
lo.IsNil(42) // false
func IsNil(x any) boolIsNotNil
IsNil 的反向操作。
lo.IsNotNil(nil) // false
lo.IsNotNil(42) // true
lo.IsNotNil("hello") // true
func IsNotNil(x any) boolToPtr
返回指向所提供值的指针(常用于需要 *T 字面量的场景)。
ptr := lo.ToPtr(42) // *int,指向 42
ptr2 := lo.ToPtr("hello") // *string,指向 "hello"
func ToPtr[T any](x T) *TFromPtr
解引用指针;若为 nil 则返回零值。
ptr := lo.ToPtr(42)
lo.FromPtr(ptr) // 42
lo.FromPtr[string](nil) // ""
func FromPtr[T any](x *T) TFromPtrOr
返回指针指向的值,若为 nil 则返回回退值。
ptr := lo.ToPtr(42)
lo.FromPtrOr(ptr, 0) // 42
lo.FromPtrOr[int](nil, -1) // -1
func FromPtrOr[T any](x *T, fallback T) TEmptyableToPtr
仅当值非零值时才转换为指针,零值时返回 nil。
lo.EmptyableToPtr("") // nil
lo.EmptyableToPtr("hello") // *string,指向 "hello"
lo.EmptyableToPtr(0) // nil
func EmptyableToPtr[T any](x T) *TToSlicePtr
将值切片转换为指针切片。
slice := []int{1, 2, 3}
ptrs := lo.ToSlicePtr(slice) // []*int{&1, &2, &3}
func ToSlicePtr[T any](collection []T) []*TFromSlicePtr
将指针切片转换回值切片,其中 nil 指针变为零值。
a, b, c := 1, 2, 3
ptrs := []*int{&a, &b, &c}
slice := lo.FromSlicePtr(ptrs) // []int{1, 2, 3}
func FromSlicePtr[T any](collection []*T) []TEmpty
返回指定类型的零值。
lo.Empty[string]() // ""
lo.Empty[int]() // 0
lo.Empty[bool]() // false
func Empty[T any]() TIsEmpty
如果值为空(零值)则返回 true。
lo.IsEmpty("") // true
lo.IsEmpty("hello") // false
lo.IsEmpty(0) // true
func IsEmpty[T comparable](v T) boolIsNotEmpty
IsEmpty 的反向操作。
lo.IsNotEmpty("") // false
lo.IsNotEmpty("hello") // true
lo.IsNotEmpty(42) // true
func IsNotEmpty[T comparable](v T) boolToAnySlice
将类型化切片转换为 []any。
ints := []int{1, 2, 3}
lo.ToAnySlice(ints) // []any{1, 2, 3}
func ToAnySlice[T any](collection []T) []anyFromAnySlice
将 []any 转换回类型化切片,附带表示是否全部断言成功的布尔值。
data := []any{1, 2, 3}
result, ok := lo.FromAnySlice[int](data) // []int{1, 2, 3}, true
func FromAnySlice[T any](in []any) ([]T, bool)Coalesce
返回参数中第一个非零值,附带表示是否找到的布尔值(类似 SQL 的 COALESCE)。
result, ok := lo.Coalesce("", "foo", "bar") // "foo", true
result2, ok2 := lo.Coalesce(0, 42, 100) // 42, true
func Coalesce[T comparable](values ...T) (T, bool)CoalesceOrEmpty
返回第一个非零值,若全部为零则返回零值(Coalesce 的简化版,省去布尔返回)。
lo.CoalesceOrEmpty("", "foo") // "foo"
lo.CoalesceOrEmpty("", "") // ""
func CoalesceOrEmpty[T comparable](v ...T) TCoalesceSlice
返回第一个非空切片,附带表示是否找到的布尔值。
result, ok := lo.CoalesceSlice([]int{}, []int{1, 2}) // []int{1, 2}, true
func CoalesceSlice[T any](v ...[]T) ([]T, bool)CoalesceSliceOrEmpty
返回第一个非空切片,若全部为空则返回空切片。
lo.CoalesceSliceOrEmpty([]int{}, []int{1, 2}) // []int{1, 2}
func CoalesceSliceOrEmpty[T any](v ...[]T) []TCoalesceMap
返回第一个非空映射,附带表示是否找到的布尔值。
result, ok := lo.CoalesceMap(map[string]int{}, map[string]int{"a": 1})
// map[string]int{"a": 1}, true
func CoalesceMap[K comparable, V any](v ...map[K]V) (map[K]V, bool)CoalesceMapOrEmpty
返回第一个非空映射,若全部为空则返回空映射。
lo.CoalesceMapOrEmpty(map[string]int{}, map[string]int{"a": 1})
// map[string]int{"a": 1}
func CoalesceMapOrEmpty[K comparable, V any](v ...map[K]V) map[K]V