samber/lo
GitHub: samber/lo
基于 Go 1.18+ 泛型的 Lodash 风格工具库,提供丰富的 slice、map、channel 及并发操作函数
Stars: 21063 | Forks: 937
# lo - 迭代 slice、map、channel...
[](https://github.com/samber/lo/releases)

[](https://pkg.go.dev/github.com/samber/lo)

[](https://goreportcard.com/report/github.com/samber/lo)
[](https://codecov.io/gh/samber/lo)
[](https://github.com/samber/lo/graphs/contributors)
[](./LICENSE)
✨ **`samber/lo` 是一个基于 Go 1.18+ 泛型的 Lodash 风格 Go 库。**
这是一个基于 Go 1.18+ 泛型的实用工具库,旨在简化 slice、map、string、channel 和 function 的操作。它提供了数十种便捷的方法来简化常见的编码任务并提高代码的可读性。在某些方面,它看起来像 [Lodash](https://github.com/lodash/lodash)。
可能有 5 到 10 个辅助函数与 Go 标准库中 `slices` 和 `maps` 包里的重叠。我认为这个库是合理的,并且提供了更多有价值的抽象。
**另请参阅:**
- [samber/ro](https://github.com/samber/ro): Go 的响应式编程:用于事件驱动应用程序的声明式和可组合 API
- [samber/do](https://github.com/samber/do): 一个基于 Go 1.18+ 泛型的依赖注入工具包
- [samber/mo](https://github.com/samber/mo): 基于 Go 1.18+ 泛型的 Monad(Option, Result, Either...)
它与 **samber/ro** 有什么不同?
- lo: 针对有限序列(map, slice...)的同步辅助函数
- ro: 针对事件驱动场景的无限数据流处理
💖 Sponsored by:
**为什么叫这个名字?**
我想要一个**简短的名字**,类似于 "Lodash",而且没有 Go 包使用这个名字。

## 🚀 安装
```
go get github.com/samber/lo@v1
```
该库处于 v1 版本并严格遵循 SemVer。
在 v2.0.0 之前,除了 `exp/` 下的实验性包外,导出的 API 不会发生破坏性更改。
该库在 Go 标准库之外没有任何依赖。
## 💡 用法
您可以使用以下方式导入 `lo`:
```
import (
"github.com/samber/lo"
lop "github.com/samber/lo/parallel"
lom "github.com/samber/lo/mutable"
loi "github.com/samber/lo/it"
)
```
然后使用下面的一个辅助函数:
```
names := lo.Uniq([]string{"Samuel", "John", "Samuel"})
// []string{"Samuel", "John"}
```
### 给懒惰开发者的建议
我不推荐这样做,但如果您懒得到处重复写 `lo.`,您可以将整个库导入到命名空间中。
```
import (
. "github.com/samber/lo"
)
```
我对这些垃圾代码不承担任何责任。😁 💩
## 🤠 规范
GoDoc: [godoc.org/github.com/samber/lo](https://godoc.org/github.com/samber/lo)
文档: [lo.samber.dev](https://lo.samber.dev/docs/about)
支持的 slice 辅助函数:
- [Filter](#filter)
- [Map](#map)
- [UniqMap](#uniqmap)
- [FilterMap](#filtermap)
- [FlatMap](#flatmap)
- [Reduce](#reduce)
- [ReduceRight](#reduceright)
- [ForEach](#foreach)
- [ForEachWhile](#foreachwhile)
- [Times](#times)
- [Uniq](#uniq)
- [UniqBy](#uniqby)
- [GroupBy](#groupby)
- [GroupByMap](#groupbymap)
- [Chunk](#chunk)
- [Window](#window)
- [Sliding](#sliding)
- [PartitionBy](#partitionby)
- [Flatten](#flatten)
- [Concat](#concat)
- [Interleave](#interleave)
- [Shuffle](#shuffle)
- [Reverse](#reverse)
- [Fill](#fill)
- [Repeat](#repeat)
- [RepeatBy](#repeatby)
- [KeyBy](#keyby)
- [SliceToMap / Associate](#slicetomap-alias-associate)
- [FilterSliceToMap](#filterslicetomap)
- [Keyify](#keyify)
- [Take](#take)
- [TakeWhile](#takewhile)
- [TakeFilter](#takefilter)
- [Drop](#drop)
- [DropRight](#dropright)
- [DropWhile](#dropwhile)
- [DropRightWhile](#droprightwhile)
- [DropByIndex](#DropByIndex)
- [Reject](#reject)
- [RejectMap](#rejectmap)
- [FilterReject](#filterreject)
- [Count](#count)
- [CountBy](#countby)
- [CountValues](#countvalues)
- [CountValuesBy](#countvaluesby)
- [Subset](#subset)
- [Slice](#slice)
- [Replace](#replace)
- [ReplaceAll](#replaceall)
- [Clone](#clone)
- [Compact](#compact)
- [IsSorted](#issorted)
- [IsSortedBy](#issortedby)
- [Splice](#Splice)
- [Cut](#Cut)
- [CutPrefix](#CutPrefix)
- [CutSuffix](#CutSuffix)
- [Trim](#Trim)
- [TrimLeft](#TrimLeft)
- [TrimPrefix](#TrimPrefix)
- [TrimRight](#TrimRight)
- [TrimSuffix](#TrimSuffix)
支持的 map 辅助函数:
- [Keys](#keys)
- [UniqKeys](#uniqkeys)
- [HasKey](#haskey)
- [ValueOr](#valueor)
- [Values](#values)
- [UniqValues](#uniqvalues)
- [PickBy](#pickby)
- [PickByKeys](#pickbykeys)
- [PickByValues](#pickbyvalues)
- [OmitBy](#omitby)
- [OmitByKeys](#omitbykeys)
- [OmitByValues](#omitbyvalues)
- [Entries / ToPairs](#entries-alias-topairs)
- [FromEntries / FromPairs](#fromentries-alias-frompairs)
- [Invert](#invert)
- [Assign (merge of maps)](#assign)
- [ChunkEntries](#chunkentries)
- [MapKeys](#mapkeys)
- [MapValues](#mapvalues)
- [MapEntries](#mapentries)
- [MapToSlice](#maptoslice)
- [FilterMapToSlice](#FilterMapToSlice)
- [FilterKeys](#FilterKeys)
- [FilterValues](#FilterValues)
支持的数学辅助函数:
- [Range / RangeFrom / RangeWithSteps](#range--rangefrom--rangewithsteps)
- [Clamp](#clamp)
- [Sum](#sum)
- [SumBy](#sumby)
- [Product](#product)
- [ProductBy](#productby)
- [Mean](#mean)
- [MeanBy](#meanby)
- [Mode](#mode)
支持的 string 辅助函数:
- [RandomString](#randomstring)
- [Substring](#substring)
- [ChunkString](#chunkstring)
- [RuneLength](#runelength)
- [PascalCase](#pascalcase)
- [CamelCase](#camelcase)
- [KebabCase](#kebabcase)
- [SnakeCase](#snakecase)
- [Words](#words)
- [Capitalize](#capitalize)
- [Ellipsis](#ellipsis)
支持的 tuple 辅助函数:
- [T2 -> T9](#t2---t9)
- [Unpack2 -> Unpack9](#unpack2---unpack9)
- [Zip2 -> Zip9](#zip2---zip9)
- [ZipBy2 -> ZipBy9](#zipby2---zipby9)
- [Unzip2 -> Unzip9](#unzip2---unzip9)
- [UnzipBy2 -> UnzipBy9](#unzipby2---unzipby9)
- [CrossJoin2 -> CrossJoin2](#crossjoin2---crossjoin9)
- [CrossJoinBy2 -> CrossJoinBy2](#crossjoinby2---crossjoinby9)
支持的时间和持续时间辅助函数:
- [Duration](#duration)
- [Duration0 -> Duration10](#duration0---duration10)
支持的 channel 辅助函数:
- [ChannelDispatcher](#channeldispatcher)
- [SliceToChannel](#slicetochannel)
- [ChannelToSlice](#channeltoslice)
- [Generator](#generator)
- [Buffer](#buffer)
- [BufferWithContext](#bufferwithcontext)
- [BufferWithTimeout](#bufferwithtimeout)
- [FanIn](#fanin)
- [FanOut](#fanout)
支持的交集辅助函数:
- [Contains](#contains)
- [ContainsBy](#containsby)
- [Every](#every)
- [EveryBy](#everyby)
- [Some](#some)
- [SomeBy](#someby)
- [None](#none)
- [NoneBy](#noneby)
- [Intersect](#intersect)
- [IntersectBy](#intersectby)
- [Difference](#difference)
- [Union](#union)
- [Without](#without)
- [WithoutBy](#withoutby)
- [WithoutEmpty](#withoutempty)
- [WithoutNth](#withoutnth)
- [ElementsMatch](#ElementsMatch)
- [ElementsMatchBy](#ElementsMatchBy)
支持的搜索辅助函数:
- [IndexOf](#indexof)
- [LastIndexOf](#lastindexof)
- [HasPrefix](#hasprefix)
- [HasSuffix](#hassuffix)
- [Find](#find)
- [FindIndexOf](#findindexof)
- [FindLastIndexOf](#findlastindexof)
- [FindOrElse](#findorelse)
- [FindKey](#findkey)
- [FindKeyBy](#findkeyby)
- [FindUniques](#finduniques)
- [FindUniquesBy](#finduniquesby)
- [FindDuplicates](#findduplicates)
- [FindDuplicatesBy](#findduplicatesby)
- [Min](#min)
- [MinIndex](#minindex)
- [MinBy](#minby)
- [MinIndexBy](#minindexby)
- [Earliest](#earliest)
- [EarliestBy](#earliestby)
- [Max](#max)
- [MaxIndex](#maxindex)
- [MaxBy](#maxby)
- [MaxIndexBy](#maxindexby)
- [Latest](#latest)
- [LatestBy](#latestby)
- [First](#first)
- [FirstOrEmpty](#FirstOrEmpty)
- [FirstOr](#FirstOr)
- [Last](#last)
- [LastOrEmpty](#LastOrEmpty)
- [LastOr](#LastOr)
- [Nth](#nth)
- [NthOr](#nthor)
- [NthOrEmpty](#nthorempty)
- [Sample](#sample)
- [SampleBy](#sampleby)
- [Samples](#samples)
- [SamplesBy](#samplesby)
条件辅助函数:
- [Ternary](#ternary)
- [TernaryF](#ternaryf)
- [If / ElseIf / Else](#if--elseif--else)
- [Switch / Case / Default](#switch--case--default)
类型操作辅助函数:
- [IsNil](#isnil)
- [IsNotNil](#isnotnil)
- [ToPtr](#toptr)
- [Nil#nil)
- [EmptyableToPtr](#emptyabletoptr)
- [FromPtr](#fromptr)
- [FromPtrOr](#fromptror)
- [ToSlicePtr](#tosliceptr)
- [FromSlicePtr](#fromsliceptr)
- [FromSlicePtrOr](#fromsliceptror)
- [ToAnySlice](#toanyslice)
- [FromAnySlice](#fromanyslice)
- [Empty](#empty)
- [IsEmpty](#isempty)
- [IsNotEmpty](#isnotempty)
- [Coalesce](#coalesce)
- [CoalesceOrEmpty](#coalesceorempty)
- [CoalesceSlice](#coalesceslice)
- [CoalesceSliceOrEmpty](#coalescesliceorempty)
- [CoalesceMap](#coalescemap)
- [CoalesceMapOrEmpty](#coalescemaporempty)
函数辅助函数:
- [Partial](#partial)
- [Partial2 -> Partial5](#partial2---partial5)
并发辅助函数:
- [Attempt](#attempt)
- [AttemptWhile](#attemptwhile)
- [AttemptWithDelay](#attemptwithdelay)
- [AttemptWhileWithDelay](#attemptwhilewithdelay)
- [Debounce](#debounce)
- [DebounceBy](#debounceby)
- [Throttle](#throttle)
- [ThrottleWithCount](#throttle)
- [ThrottleBy](#throttle)
- [ThrottleByWithCount](#throttle)
- [Synchronize](#synchronize)
- [Async](#async)
- [Async{0->6}](#async0-6)
- [Transaction](#transaction)
- [WaitFor](#waitfor)
- [WaitForWithContext](#waitforwithcontext)
错误处理:
- [Validate](#validate)
- [Must](#must)
- [Try](#try)
- [Try1 -> Try6](#try0-6)
- [TryOr](#tryor)
- [TryOr1 -> TryOr6](#tryor0-6)
- [TryCatch](#trycatch)
- [TryWithErrorValue](#trywitherrorvalue)
- [TryCatchWithErrorValue](#trycatchwitherrorvalue)
- [ErrorsAs](#errorsas)
- [Assert](#assert)
- [Assertf](#assertf)
约束:
- Clonable
### Filter
遍历一个集合,并返回由断言函数返回 `true` 的所有元素组成的 slice。
```
even := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {
return x%2 == 0
})
// []int{2, 4}
```
```
// Use FilterErr when the predicate can return an error
even, err := lo.FilterErr([]int{1, 2, 3, 4}, func(x int, _ int) (bool, error) {
if x == 3 {
return false, fmt.Errorf("number 3 is not allowed")
}
return x%2 == 0, nil
})
// []int(nil), error("number 3 is not allowed")
```
[[play](https://go.dev/play/p/Apjg3WeSi7K)]
可变:类似于 `lo.Filter()`,但 slice 是原地更新的。
```
import lom "github.com/samber/lo/mutable"
list := []int{1, 2, 3, 4}
newList := lom.Filter(list, func(x int) bool {
return x%2 == 0
})
list
// []int{2, 4, 3, 4}
newList
// []int{2, 4}
```
### Map
操作一种类型的 slice 并将其转换为另一种类型的 slice:
```
import "github.com/samber/lo"
lo.Map([]int64{1, 2, 3, 4}, func(x int64, index int) string {
return strconv.FormatInt(x, 10)
})
// []string{"1", "2", "3", "4"}
```
```
// Use MapErr when the transform function can return an error
result, err := lo.MapErr([]int{1, 2, 3, 4}, func(x int, _ int) (string, error) {
if x == 3 {
return "", fmt.Errorf("number 3 is not allowed")
}
return strconv.Itoa(x), nil
})
// []string(nil), error("number 3 is not allowed")
```
[[play](https://go.dev/play/p/OkPcYAhBo0D)]
并行处理:类似于 `lo.Map()`,但转换函数在 goroutine 中调用。结果以相同的顺序返回。
```
import lop "github.com/samber/lo/parallel"
lop.Map([]int64{1, 2, 3, 4}, func(x int64, _ int) string {
return strconv.FormatInt(x, 10)
})
// []string{"1", "2", "3", "4"}
```
[[play](https://go.dev/play/p/sCJaB3quRMC)]
可变:类似于 `lo.Map()`,但 slice 是原地更新的。
```
import lom "github.com/samber/lo/mutable"
list := []int{1, 2, 3, 4}
lom.Map(list, func(x int) int {
return x*2
})
// []int{2, 4, 6, 8}
```
[[play](https://go.dev/play/p/0jY3Z0B7O_5)]
### UniqMap
操作一个 slice 并将其转换为另一个具有唯一值的类型的 slice。
```
type User struct {
Name string
Age int
}
users := []User{{Name: "Alex", Age: 10}, {Name: "Alex", Age: 12}, {Name: "Bob", Age: 11}, {Name: "Alice", Age: 20}}
names := lo.UniqMap(users, func(u User, index int) string {
return u.Name
})
// []string{"Alex", "Bob", "Alice"}
```
[[play](https://go.dev/play/p/fygzLBhvUdB)]
### FilterMap
返回经过给定回调函数进行过滤和映射后获得的 slice。
回调函数应返回两个值:映射操作的结果以及是否应包含结果元素。
```
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"}
```
[[play](https://go.dev/play/p/-AuYXfy7opz)]
### FlatMap
操作一个 slice,并将其转换和展平为另一种类型的 slice。转换函数可以返回一个 slice 或 `nil`,如果是 `nil`,则不会向最终 slice 添加任何值。
```
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"}
```
```
// Use FlatMapErr when the transform function can return an error
result, err := lo.FlatMapErr([]int64{0, 1, 2, 3}, func(x int64, _ int) ([]string, error) {
if x == 2 {
return nil, fmt.Errorf("number 2 is not allowed")
}
return []string{strconv.FormatInt(x, 10), strconv.FormatInt(x, 10)}, nil
})
// []string(nil), error("number 2 is not allowed")
```
[[play](https://go.dev/play/p/YSoYmQTA8-U)]
### Reduce
将集合缩减为单个值。该值是通过累积器函数运行集合中的每个元素的结果来计算的。每次连续调用都会提供上一次调用返回的返回值。
```
sum := lo.Reduce([]int{1, 2, 3, 4}, func(agg int, item int, _ int) int {
return agg + item
}, 0)
// 10
```
```
// Use ReduceErr when the accumulator function can return an error
result, err := lo.ReduceErr([]int{1, 2, 3, 4}, func(agg int, item int, _ int) (int, error) {
if item == 3 {
return 0, fmt.Errorf("number 3 is not allowed")
}
return agg + item, nil
}, 0)
// 0, error("number 3 is not allowed")
```
[[play](https://go.dev/play/p/R4UHXZNaaUG)]
### ReduceRight
与 `lo.Reduce` 类似,不同之处在于它从右到左遍历集合元素。
```
result := lo.ReduceRight([][]int{{0, 1}, {2, 3}, {4, 5}}, func(agg []int, item []int, _ int) []int {
return append(agg, item...)
}, []int{})
// []int{4, 5, 2, 3, 0, 1}
```
```
// Use ReduceRightErr when the accumulator function can return an error
result, err := lo.ReduceRightErr([]int{1, 2, 3, 4}, func(agg int, item int, _ int) (int, error) {
if item == 2 {
return 0, fmt.Errorf("number 2 is not allowed")
}
return agg + item, nil
}, 0)
// 0, error("number 2 is not allowed")
```
[[play](https://go.dev/play/p/Fq3W70l7wXF)]
### ForEach
遍历集合的元素并对每个元素调用该函数。
```
import "github.com/samber/lo"
lo.ForEach([]string{"hello", "world"}, func(x string, _ int) {
println(x)
})
// prints "hello\nworld\n"
```
[[play](https://go.dev/play/p/oofyiUPRf8t)]
并行处理:类似于 `lo.ForEach()`,但回调作为 goroutine 调用。
```
import lop "github.com/samber/lo/parallel"
lop.ForEach([]string{"hello", "world"}, func(x string, _ int) {
println(x)
})
// prints "hello\nworld\n" or "world\nhello\n"
```
### ForEachWhile
遍历集合元素并为每个元素调用 iteratee,集合的返回值决定是继续还是中断,类似于 do while()。
```
list := []int64{1, 2, -42, 4}
lo.ForEachWhile(list, func(x int64, _ int) bool {
if x < 0 {
return false
}
fmt.Println(x)
return true
})
// 1
// 2
```
[[play](https://go.dev/play/p/QnLGt35tnow)]
### Times
Times 调用 iteratee n 次,返回每次调用结果的 slice。iteratee 以索引作为参数调用。
```
import "github.com/samber/lo"
lo.Times(3, func(i int) string {
return strconv.FormatInt(int64(i), 10)
})
// []string{"0", "1", "2"}
```
[[play](https://go.dev/play/p/vgQj3Glr6lT)]
并行处理:类似于 `lo.Times()`,但回调在 goroutine 中调用。
```
import lop "github.com/samber/lo/parallel"
lop.Times(3, func(i int) string {
return strconv.FormatInt(int64(i), 10)
})
// []string{"0", "1", "2"}
```
### Uniq
返回 slice 的去重版本,其中仅保留每个元素的第一次出现。结果值的顺序由它们在 slice 中出现的顺序决定。
```
uniqValues := lo.Uniq([]int{1, 2, 2, 1})
// []int{1, 2}
```
[[play](https://go.dev/play/p/DTzbeXZ6iEN)]
### UniqBy
返回 slice 的去重版本,其中仅保留每个元素的第一次出现。结果值的顺序由它们在 slice 中出现的顺序决定。它接受 `iteratee`,该函数对 slice 中的每个元素调用以生成计算唯一性的标准。
```
uniqValues := lo.UniqBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
return i%3
})
// []int{0, 1, 2}
```
```
// Use UniqByErr when the iteratee function can return an error
result, err := lo.UniqByErr([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, error) {
if i == 3 {
return 0, fmt.Errorf("number 3 is not allowed")
}
return i % 3, nil
})
// []int(nil), error("number 3 is not allowed")
```
[[play](https://go.dev/play/p/g42Z3QSb53u)]
### GroupBy
返回一个由通过 iteratee 运行集合的每个元素的结果生成的键组成的对象。
```
import lo "github.com/samber/lo"
groups := lo.GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
return i%3
})
// map[int][]int{0: []int{0, 3}, 1: []int{1, 4}, 2: []int{2, 5}}
```
```
// Use GroupByErr when the iteratee function can return an error
result, err := lo.GroupByErr([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, error) {
if i == 3 {
return 0, fmt.Errorf("number 3 is not allowed")
}
return i % 3, nil
})
// map[int][]int(nil), error("number 3 is not allowed")
```
[[play](https://go.dev/play/p/XnQBd_v6brd)]
并行处理:类似于 `lo.GroupBy()`,但回调在 goroutine 中调用。
```
import lop "github.com/samber/lo/parallel"
lop.GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
return i%3
})
// map[int][]int{0: []int{0, 3}, 1: []int{1, 4}, 2: []int{2, 5}}
```
### GroupByMap
返回一个由通过 iteratee 运行集合的每个元素的结果生成的键组成的对象。
```
import lo "github.com/samber/lo"
groups := lo.GroupByMap([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, int) {
return i%3, i*2
})
// map[int][]int{0: []int{0, 6}, 1: []int{2, 8}, 2: []int{4, 10}}
```
```
// Use GroupByMapErr when the transform function can return an error
result, err := lo.GroupByMapErr([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, int, error) {
if i == 3 {
return 0, 0, fmt.Errorf("number 3 is not allowed")
}
return i % 3, i * 2, nil
})
// map[int][]int(nil), error("number 3 is not allowed")
```
[[play](https://go.dev/play/p/iMeruQ3_W80)]
### Chunk
返回一个被分割成长度为 size 的组的元素 slice。如果 slice 不能被均匀分割,则最后的块将是剩余的元素。
```
lo.Chunk([]int{0, 1, 2, 3, 4, 5}, 2)
// [][]int{{0, 1}, {2, 3}, {4, 5}}
lo.Chunk([]int{0, 1, 2, 3, 4, 5, 6}, 2)
// [][]int{{0, 1}, {2, 3}, {4, 5}, {6}}
lo.Chunk([]int{}, 2)
// [][]int{}
lo.Chunk([]int{0}, 2)
// [][]int{{0}}
```
[[play](https://go.dev/play/p/kEMkFbdu85g)]
### Window
创建给定大小的滑动窗口 slice。每个窗口与前一个窗口共享 size-1 个元素。这等同于 `Sliding(collection, size, 1)`。
```
lo.Window([]int{1, 2, 3, 4, 5}, 3)
// [][]int{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}}
lo.Window([]float64{20, 22, 21, 23, 24}, 3)
// [][]float64{{20, 22, 21}, {22, 21, 23}, {21, 23, 24}}
```
### Sliding
创建具有给定大小和步长的滑动窗口 slice。如果步长等于大小,则窗口没有公共元素(类似于 Chunk)。如果步长小于大小,则窗口共享公共元素。
```
// Windows with shared elements (step < size)
lo.Sliding([]int{1, 2, 3, 4, 5, 6}, 3, 1)
// [][]int{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}, {4, 5, 6}}
// Windows with no shared elements (step == size, like Chunk)
lo.Sliding([]int{1, 2, 3, 4, 5, 6}, 3, 3)
// [][]int{{1, 2, 3}, {4, 5, 6}}
// Step > size (skipping elements)
lo.Sliding([]int{1, 2, 3, 4, 5, 6, 7, 8}, 2, 3)
// [][]int{{1, 2}, {4, 5}, {7, 8}}
```
### PartitionBy
返回一个被分割成组的元素 slice。分组值的顺序由它们在集合中出现的顺序决定。分组是通过 iteratee 运行集合的每个元素的结果生成的。
```
import lo "github.com/samber/lo"
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"
})
// [][]int{{-2, -1}, {0, 2, 4}, {1, 3, 5}}
```
```
// Use PartitionByErr when the iteratee function can return an error
result, err := lo.PartitionByErr([]int{-2, -1, 0, 1, 2}, func(x int) (string, error) {
if x == 0 {
return "", fmt.Errorf("zero is not allowed")
}
if x < 0 {
return "negative", nil
} else if x%2 == 0 {
return "even", nil
}
return "odd", nil
})
// [][]int(nil), error("zero is not allowed")
```
[[play](https://go.dev/play/p/NfQ_nGjkgXW)]
并行处理:类似于 `lo.PartitionBy()`,但回调在 goroutine 中调用。结果以相同的顺序返回。
```
import lop "github.com/samber/lo/parallel"
partitions := 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"
})
// [][]int{{-2, -1}, {0, 2, 4}, {1, 3, 5}}
```
### Flatten
返回展开一层的 slice。
```
flat := lo.Flatten([][]int{{0, 1}, {2, 3, 4, 5}})
// []int{0, 1, 2, 3, 4, 5}
```
[[play](https://go.dev/play/p/rbp9ORaMpjw)]
### Concat
返回一个包含 collections 中所有元素的新 slice。Concat 保持元素的顺序。
```
slice := lo.Concat([]int{1, 2}, []int{3, 4})
// []int{1, 2, 3, 4}
slice := lo.Concat(nil, []int{1, 2}, nil, []int{3, 4}, nil)
// []int{1, 2, 3, 4}
slice := lo.Concat[int]()
// []int{}
```
### Interleave
轮流交替输入 slice,并按顺序将索引处的值追加到结果中。
```
interleaved := lo.Interleave([]int{1, 4, 7}, []int{2, 5, 8}, []int{3, 6, 9})
// []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
interleaved := lo.Interleave([]int{1}, []int{2, 5, 8}, []int{3, 6}, []int{4, 7, 9, 10})
// []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
```
[[play](https://go.dev/play/p/-RJkTLQEDVt)]
### Shuffle
返回一个打乱顺序的值 slice。使用 Fisher-Yates 洗牌算法。
⚠️ 此辅助函数是**可变的**。
```
import lom "github.com/samber/lo/mutable"
list := []int{0, 1, 2, 3, 4, 5}
lom.Shuffle(list)
list
// []int{1, 4, 0, 3, 5, 2}
```
[[play](https://go.dev/play/p/2xb3WdLjeSJ)]
### Reverse
反转 slice,使第一个元素变为最后一个,第二个元素变为倒数第二个,依此类推。
⚠️ 此辅助函数是**可变的**。
```
import lom "github.com/samber/lo/mutable"
list := []int{0, 1, 2, 3, 4, 5}
lom.Reverse(list)
list
// []int{5, 4, 3, 2, 1, 0}
```
[[play](https://go.dev/play/p/O-M5pmCRgzV)]
### Fill
用 `initial` 值填充 slice 的元素。
```
type foo struct {
bar string
}
func (f foo) Clone() foo {
return foo{f.bar}
}
initializedSlice := lo.Fill([]foo{foo{"a"}, foo{"a"}}, foo{"b"})
// []foo{foo{"b"}, foo{"b"}}
```
[[play](https://go.dev/play/p/VwR34GzqEub)]
### Repeat
构建一个包含 N 个初始值副本的 slice。
```
type foo struct {
bar string
}
func (f foo) Clone() foo {
return foo{f.bar}
}
slice := lo.Repeat(2, foo{"a"})
// []foo{foo{"a"}, foo{"a"}}
```
[[play](https://go.dev/play/p/g3uHXbmc3b6)]
### RepeatBy
构建一个包含 N 次回调调用返回值的 slice。
```
slice := lo.RepeatBy(0, func (i int) string {
return strconv.FormatInt(int64(math.Pow(float64(i), 2)), 10)
})
// []string{}
slice := lo.RepeatBy(5, func(i int) string {
return strconv.FormatInt(int64(math.Pow(float64(i), 2)), 10)
})
// []string{"0", "1", "4", "9", "16"}
```
[[play](https://go.dev/play/p/ozZLCtX_hNU)]
带错误处理:
```
slice, err := lo.RepeatByErr(5, func(i int) (string, error) {
if i == 3 {
return "", fmt.Errorf("index 3 is not allowed")
}
return fmt.Sprintf("item-%d", i), nil
})
// []string(nil), error("index 3 is not allowed")
```
### KeyBy
基于 pivot 回调将 slice 或结构体 slice 转换为 map。
```
m := lo.KeyBy([]string{"a", "aa", "aaa"}, func(str string) int {
return len(str)
})
// map[int]string{1: "a", 2: "aa", 3: "aaa"}
type Character struct {
dir string
code int
}
characters := []Character{
{dir: "left", code: 97},
{dir: "right", code: 100},
}
result := lo.KeyBy(characters, func(char Character) string {
return string(rune(char.code))
})
//map[a:{dir:left code:97} d:{dir:right code:100}]
```
```
result, err := lo.KeyByErr([]string{"a", "aa", "aaa", ""}, func(str string) (int, error) {
if str == "" {
return 0, fmt.Errorf("empty string not allowed")
}
return len(str), nil
})
// map[int]string(nil), error("empty string not allowed")
```
[[play](https://go.dev/play/p/mdaClUAT-zZ)]
### SliceToMap (别名: Associate)
返回一个 map,其中包含应用于给定 slice 元素的转换函数提供的键值对。
如果两对中的任何一对具有相同的键,则最后一对将被添加到 map 中。
返回 map 中键的顺序未指定,并且不保证与原始 slice 中的顺序相同。
```
in := []*foo{{baz: "apple", bar: 1}, {baz: "banana", bar: 2}}
aMap := lo.SliceToMap(in, func (f *foo) (string, int) {
return f.baz, f.bar
})
// map[string][int]{ "apple":1, "banana":2 }
```
[[play](https://go.dev/play/p/WHa2CfMO3Lr)]
### FilterSliceToMap
返回一个 map,其中包含应用于给定 slice 元素的转换函数提供的键值对。
如果两对中的任何一对具有相同的键,则最后一对将被添加到 map 中。
返回 map 中键的顺序未指定,并且不保证与原始 slice 中的顺序相同。
转换函数的第三个返回值是一个布尔值,指示是否应将键值对包含在 map 中。
```
list := []string{"a", "aa", "aaa"}
result := lo.FilterSliceToMap(list, func(str string) (string, int, bool) {
return str, len(str), len(str) > 1
})
// map[string][int]{"aa":2 "aaa":3}
```
[[play](https://go.dev/play/p/2z0rDz2ZSGU)]
### Keyify
返回一个 map,其中以 slice 的每个唯一元素作为键。
```
set := lo.Keyify([]int{1, 1, 2, 3, 4})
// map[int]struct{}{1:{}, 2:{}, 3:{}, 4:{}}
```
[[play](https://go.dev/play/p/RYhhM_csqIG)]
### Take
从 slice 中获取前 n 个元素。
```
l := lo.Take([]int{0, 1, 2, 3, 4, 5}, 3)
// []int{0, 1, 2}
l := lo.Take([]int{0, 1, 2}, 5)
// []int{0, 1, 2}
```
### TakeWhile
当断言返回 true 时,从开头获取元素。
```
l := lo.TakeWhile([]int{0, 1, 2, 3, 4, 5}, func(val int) bool {
return val < 3
})
// []int{0, 1, 2}
l := lo.TakeWhile([]string{"a", "aa", "aaa", "aa"}, func(val string) bool {
return len(val) <= 2
})
// []string{"a", "aa"}
```
### TakeFilter
过滤元素并获取前 n 个匹配断言的元素。等同于调用 Take(Filter(...)),但更高效,因为它在找到 n 个匹配项后停止。
```
l := lo.TakeFilter([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 3, func(val int, index int) bool {
return val%2 == 0
})
// []int{2, 4, 6}
l := lo.TakeFilter([]string{"a", "aa", "aaa", "aaaa"}, 2, func(val string, index int) bool {
return len(val) > 1
})
// []string{"aa", "aaa"}
```
### Drop
从 slice 开头丢弃 n 个元素。
```
l := lo.Drop([]int{0, 1, 2, 3, 4, 5}, 2)
// []int{2, 3, 4, 5}
```
[[play](https://go.dev/play/p/JswS7vXRJP2)]
### DropRight
从 slice 末尾丢弃 n 个元素。
```
l := lo.DropRight([]int{0, 1, 2, 3, 4, 5}, 2)
// []int{0, 1, 2, 3}
```
[[play](https://go.dev/play/p/GG0nXkSJJa3)]
### DropWhile
当断言返回 true 时,从 slice 开头丢弃元素。
```
l := lo.DropWhile([]string{"a", "aa", "aaa", "aa", "aa"}, func(val string) bool {
return len(val) <= 2
})
// []string{"aaa", "aa", "aa"}
```
[[play](https://go.dev/play/p/7gBPYw2IK16)]
### DropRightWhile
当断言返回 true 时,从 slice 末尾丢弃元素。
```
l := lo.DropRightWhile([]string{"a", "aa", "aaa", "aa", "aa"}, func(val string) bool {
return len(val) <= 2
})
// []string{"a", "aa", "aaa"}
```
[[play](https://go.dev/play/p/3-n71oEC0Hz)]
### DropByIndex
通过索引从 slice 中丢弃元素。负索引将从 slice 末尾丢弃元素。
```
l := lo.DropByIndex([]int{0, 1, 2, 3, 4, 5}, 2, 4, -1)
// []int{0, 1, 3}
```
[[play](https://go.dev/play/p/JswS7vXRJP2)]
### Reject
Filter 的相反方法,此方法返回断言未返回 true 的集合元素。
```
odd := lo.Reject([]int{1, 2, 3, 4}, func(x int, _ int) bool {
return x%2 == 0
})
// []int{1, 3}
```
```
// Use RejectErr when the predicate can return an error
odd, err := lo.RejectErr([]int{1, 2, 3, 4}, func(x int, _ int) (bool, error) {
if x == 3 {
return false, fmt.Errorf("number 3 is not allowed")
}
return x%2 == 0, nil
})
// []int(nil), error("number 3 is not allowed")
```
[[play](https://go.dev/play/p/YkLMODy1WEL)]
### RejectMap
FilterMap 的相反方法,此方法返回经过给定回调函数进行过滤和映射后获得的 slice。
回调函数应返回两个值:
- 映射操作的结果
- 是否应包含结果元素。
```
items := lo.RejectMap([]int{1, 2, 3, 4}, func(x int, _ int) (int, bool) {
return x*10, x%2 == 0
})
// []int{10, 30}
```
### FilterReject
混合了 Filter 和 Reject,此方法返回两个 slice,一个用于断言返回 true 的集合元素,另一个用于断言未返回 true 的元素。
```
kept, rejected := lo.FilterReject([]int{1, 2, 3, 4}, func(x int, _ int) bool {
return x%2 == 0
})
// []int{2, 4}
// []int{1, 3}
```
### Count
计算集合中等于 value 的元素数量。
```
count := lo.Count([]int{1, 5, 1}, 1)
// 2
```
[[play](https://go.dev/play/p/Y3FlK54yveC)]
### CountBy
计算集合中断言为 true 的元素数量。
```
count := lo.CountBy([]int{1, 5, 1}, func(i int) bool {
return i < 4
})
// 2
```
```
// Use CountByErr when the predicate can return an error
count, err := lo.CountByErr([]int{1, 5, 1}, func(i int) (bool, error) {
if i == 5 {
return false, fmt.Errorf("5 not allowed")
}
return i < 4, nil
})
// 0, error("5 not allowed")
```
[[play](https://go.dev/play/p/ByQbNYQQi4X)]
### CountValues
计算集合中每个元素的数量。
```
lo.CountValues([]int{})
// map[int]int{}
lo.CountValues([]int{1, 2})
// map[int]int{1: 1, 2: 1}
lo.CountValues([]int{1, 2, 2})
// map[int]int{1: 1, 2: 2}
lo.CountValues([]string{"foo", "bar", ""})
// map[string]int{"": 1, "foo": 1, "bar": 1}
lo.CountValues([]string{"foo", "bar", "bar"})
// map[string]int{"foo": 1, "bar": 2}
```
[[play](https://go.dev/play/p/-p-PyLT4dfy)]
### CountValuesBy
计算集合中每个元素的数量。它等同于链式调用 lo.Map 和 lo.CountValues。
```
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}
```
[[play](https://go.dev/play/p/2U0dG1SnOmS)]
### Subset
返回从 `offset` 开始最多 `length` 个元素的 slice 副本。类似于 `slice[start:start+length]`,但在溢出时不会 panic。
```
in := []int{0, 1, 2, 3, 4}
sub := lo.Subset(in, 2, 3)
// []int{2, 3, 4}
sub := lo.Subset(in, -4, 3)
// []int{1, 2, 3}
sub := lo.Subset(in, -2, math.MaxUint)
// []int{3, 4}
```
[[play](https://go.dev/play/p/tOQu1GhFcog)]
### Slice
返回从 `start` 开始到(但不包括)`end` 的 slice 副本。类似于 `slice[start:end]`,但在溢出时不会 panic。
```
in := []int{0, 1, 2, 3, 4}
slice := lo.Slice(in, 0, 5)
// []int{0, 1, 2, 3, 4}
slice := lo.Slice(in, 2, 3)
// []int{2}
slice := lo.Slice(in, 2, 6)
// []int{2, 3, 4}
slice := lo.Slice(in, 4, 3)
// []int{}
```
[[play](https://go.dev/play/p/8XWYhfMMA1h)]
### Replace
返回 slice 的副本,其中前 n 个非重叠的 old 实例被替换为 new。
```
in := []int{0, 1, 0, 1, 2, 3, 0}
slice := lo.Replace(in, 0, 42, 1)
// []int{42, 1, 0, 1, 2, 3, 0}
slice := lo.Replace(in, -1, 42, 1)
// []int{0, 1, 0, 1, 2, 3, 0}
slice := lo.Replace(in, 0, 42, 2)
// []int{42, 1, 42, 1, 2, 3, 0}
slice := lo.Replace(in, 0, 42, -1)
// []int{42, 1, 42, 1, 2, 3, 42}
```
[[play](https://go.dev/play/p/XfPzmf9gql6)]
### ReplaceAll
返回 slice 的副本,其中所有非重叠的 old 实例被替换为 new。
```
in := []int{0, 1, 0, 1, 2, 3, 0}
slice := lo.ReplaceAll(in, 0, 42)
// []int{42, 1, 42, 1, 2, 3, 42}
slice := lo.ReplaceAll(in, -1, 42)
// []int{0, 1, 0, 1, 2, 3, 0}
```
[[play](https://go.dev/play/p/a9xZFUHfYcV)]
### Clone
返回集合的浅拷贝。
```
in := []int{1, 2, 3, 4, 5}
cloned := lo.Clone(in)
// Verify it's a different slice by checking that modifying one doesn't affect the other
in[0] = 99
// cloned is []int{1, 2, 3, 4, 5}
```
[[play](https://go.dev/play/p/hgHmoOIxmuH)]
### Compact
返回所有零元素的 slice。
```
in := []string{"", "foo", "", "bar", ""}
slice := lo.Compact(in)
// []string{"foo", "bar"}
```
[[play](https://go.dev/play/p/tXiy-iK6PAc)]
### IsSorted
检查 slice 是否已排序。
```
slice := lo.IsSorted([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
// true
```
[[play](https://go.dev/play/p/mc3qR-t4mcx)]
### IsSortedBy
检查 slice 是否按 iteratee 排序。
```
slice := lo.IsSortedBy([]string{"a", "bb", "ccc"}, func(s string) int {
return len(s)
})
// true
```
[[play](https://go.dev/play/p/wiG6XyBBu49)]
### Splice
Splice 在索引 i 处插入多个元素。负索引从 slice 末尾开始计算。该辅助函数可防止溢出错误。
```
result := lo.Splice([]string{"a", "b"}, 1, "1", "2")
// []string{"a", "1", "2", "b"}
// negative
result = lo.Splice([]string{"a", "b"}, -1, "1", "2")
// []string{"a", "1", "2", "b"}
// overflow
result = lo.Splice([]string{"a", "b"}, 42, "1", "2")
// []string{"a", "b", "1", "2"}
```
[[play](https://go.dev/play/p/G5_GhkeSUBA)]
### Cut
在第一个分隔符实例周围切割集合,返回分隔符之前和之后的集合部分。found 结果报告分隔符是否出现在集合中。如果分隔符未出现在 s 中,cut 返回 collection、[]T 的空 slice、false。
```
actualLeft, actualRight, result = lo.Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b", "c", "d"})
// actualLeft: []string{"a"}
// actualRight: []string{"e", "f", "g"}
// result: true
result = lo.Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"z"})
// actualLeft: []string{"a", "b", "c", "d", "e", "f", "g"}
// actualRight: []string{}
// result: false
result = lo.Cut([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b"})
// actualLeft: []string{}
// actualRight: []string{"c", "d", "e", "f", "g"}
// result: true
```
[[play](https://go.dev/play/p/GiL3qhpIP3f)]
### CutPrefix
返回不包含提供的前导 prefix []T 的集合,并报告是否找到了前缀。如果 s 不是以 prefix 开头,CutPrefix 返回 collection, false。如果 prefix 是空的 []T,CutPrefix 返回 collection, true。
```
actualRight, result = lo.CutPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"a", "b", "c"})
// actualRight: []string{"d", "e", "f", "g"}
// result: true
result = lo.CutPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b"})
// actualRight: []string{"a", "b", "c", "d", "e", "f", "g"}
// result: false
result = lo.CutPrefix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
// actualRight: []string{"a", "b", "c", "d", "e", "f", "g"}
// result: true
```
[[play](https://go.dev/play/p/7Plak4a1ICl)]
### CutSuffix
返回不包含提供的结尾 suffix []T 的集合,并报告是否找到了后缀。如果它不是以 suffix 结尾,CutSuffix 返回 collection, false。如果 suffix 是空的 []T,CutSuffix 返回 collection, true。
```
actualLeft, result = lo.CutSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"f", "g"})
// actualLeft: []string{"a", "b", "c", "d", "e"}
// result: true
actualLeft, result = lo.CutSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{"b"})
// actualLeft: []string{"a", "b", "c", "d", "e", "f", "g"}
// result: false
actualLeft, result = lo.CutSuffix([]string{"a", "b", "c", "d", "e", "f", "g"}, []string{})
// actualLeft: []string{"a", "b", "c", "d", "e", "f", "g"}
// result: true
```
[[play](https://go.dev/play/p/7FKfBFvPTaT)]
### Trim
从集合中删除所有前导和尾随 cutset。
```
result := lo.Trim([]int{0, 1, 2, 0, 3, 0}, []int{1, 0})
// []int{2, 0, 3}
result := lo.Trim([]string{"hello", "world", " "}, []string{" ", ""})
// []string{"hello", "world"}
```
[[play](https://go.dev/play/p/1an9mxLdRG5)]
### TrimLeft
从集合中删除所有前导 cutset。
```
result := lo.TrimLeft([]int{0, 1, 2, 0, 3, 0}, []int{1, 0})
// []int{2, 0, 3, 0}
result := lo.TrimLeft([]string{"hello", "world", " "}, []string{" ", ""})
// []string{"hello", "world", " "}
```
[[play](https://go.dev/play/p/74aqfAYLmyi)]
### TrimPrefix
从集合中删除所有前导 prefix。
```
result := lo.TrimPrefix([]int{1, 2, 1, 2, 3, 1, 2, 4}, []int{1, 2})
// []int{3, 1, 2, 4}
result := lo.TrimPrefix([]string{"hello", "world", "hello", "test"}, []string{"hello"})
// []string{"world", "hello", "test"}
```
[[play](https://go.dev/play/p/SHO6X-YegPg)]
### TrimRight
从集合中删除所有尾随 cutset。
```
result := lo.TrimRight([]int{0, 1, 2, 0, 3, 0}, []int{0, 3})
// []int{0, 1, 2}
result := lo.TrimRight([]string{"hello", "world", " "}, []string{" ", ""})
// []string{"hello", "world", ""}
```
[[play](https://go.dev/play/p/MRpAfR6sf0g)]
### TrimSuffix
从集合中删除所有尾随 suffix。
```
result := lo.TrimSuffix([]int{1, 2, 3, 1, 2, 4, 2, 4, 2, 4}, []int{2, 4})
// []int{1, 2, 3, 1}
result := lo.TrimSuffix([]string{"hello", "world", "hello", "test"}, []string{"test"})
// []string{"hello", "world", "hello"}
```
[[play](https://go.dev/play/p/IjEUrV0iofq)]
### Keys
创建一个由 map 键组成的 slice。
使用 UniqKeys 变体来去除重复的公共键。
```
keys := lo.Keys(map[string]int{"foo": 1, "bar": 2})
// []string{"foo", "bar"}
keys := lo.Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3})
// []string{"foo", "bar", "baz"}
keys := lo.Keys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 3})
// []string{"foo", "bar", "bar"}
```
[[play](https://go.dev/play/p/Uu11fHASqrU)]
### UniqKeys
创建一个由唯一 map 键组成的 slice。
```
keys := lo.UniqKeys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3})
// []string{"foo", "bar", "baz"}
keys := lo.UniqKeys(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 3})
// []string{"foo", "bar"}
```
[[play](https://go.dev/play/p/TPKAb6ILdHk)]
### HasKey
返回给定的键是否存在。
```
exists := lo.HasKey(map[string]int{"foo": 1, "bar": 2}, "foo")
// true
exists := lo.HasKey(map[string]int{"foo": 1, "bar": 2}, "baz")
// false
```
[[play](https://go.dev/play/p/aVwubIvECqS)]
### Values
创建一个由 map 值组成的 slice。
使用 UniqValues 变体来去除重复的公共值。
```
values := lo.Values(map[string]int{"foo": 1, "bar": 2})
// []int{1, 2}
values := lo.Values(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3})
// []int{1, 2, 3}
values := lo.Values(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 2})
// []int{1, 2, 2}
```
[[play](https://go.dev/play/p/nnRTQkzQfF6)]
### UniqValues
创建一个由唯一 map 值组成的 slice。
```
values := lo.UniqValues(map[string]int{"foo": 1, "bar": 2})
// []int{1, 2}
values := lo.UniqValues(map[string]int{"foo": 1, "bar": 2}, map[string]int{"baz": 3})
// []int{1, 2, 3}
values := lo.UniqValues(map[string]int{"foo": 1, "bar": 2}, map[string]int{"bar": 2})
// []int{1, 2}
```
[[play](https://go.dev/play/p/nf6bXMh7rM3)]
### ValueOr
返回给定键的值,如果键不存在则返回回退值。
```
value := lo.ValueOr(map[string]int{"foo": 1, "bar": 2}, "foo", 42)
// 1
value := lo.ValueOr(map[string]int{"foo": 1, "bar": 2}, "baz", 42)
// 42
```
[[play](https://go.dev/play/p/bAq9mHErB4V)]
### PickBy
返回由给定断言过滤的相同 map 类型。
```
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}
```
```
// Use PickByErr when the predicate can return an error
m, err := lo.PickByErr(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(key string, value int) (bool, error) {
if key == "bar" {
return false, fmt.Errorf("bar not allowed")
}
return value%2 == 1, nil
})
// map[string]int(nil), error("bar not allowed")
```
[[play](https://go.dev/play/p/kdg8GR_QMmf)]
### PickByKeys
返回由给定键过滤的相同 map 类型。
```
m := lo.PickByKeys(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []string{"foo", "baz"})
// map[string]int{"foo": 1, "baz": 3}
```
[[play](https://go.dev/play/p/R1imbuci9qU)]
### PickByValues
返回由给定值过滤的相同 map 类型。
```
m := lo.PickByValues(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []int{1, 3})
// map[string]int{"foo": 1, "baz": 3}
```
[[play](https://go.dev/play/p/1zdzSvbfsJc)]
### OmitBy
返回由给定断言过滤的相同 map 类型。
```
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}
```
```
// Use OmitByErr when the predicate can return an error
m, err := lo.OmitByErr(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(key string, value int) (bool, error) {
if key == "bar" {
return false, fmt.Errorf("bar not allowed")
}
return value%2 == 1, nil
})
// map[string]int(nil), error("bar not allowed")
```
[[play](https://go.dev/play/p/EtBsR43bdsd)]
### OmitByKeys
返回由给定键过滤的相同 map 类型。
```
m := lo.OmitByKeys(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []string{"foo", "baz"})
// map[string]int{"bar": 2}
```
[[play](https://go.dev/play/p/t1QjCrs-ysk)]
### OmitByValues
返回由给定值过滤的相同 map 类型。
```
m := lo.OmitByValues(map[string]int{"foo": 1, "bar": 2, "baz": 3}, []int{1, 3})
// map[string]int{"bar": 2}
```
[[play](https://go.dev/play/p/9UYZi-hrs8j)]
### Entries (别名: ToPairs)
将 map 转换为键/值对的 slice。
```
entries := lo.Entries(map[string]int{"foo": 1, "bar": 2})
// []lo.Entry[string, int]{
// {
// Key: "foo",
// Value: 1,
// },
// {
// Key: "bar",
// Value: 2,
// },
// }
```
[[play](https://go.dev/play/p/_t4Xe34-Nl5)]
### FromEntries (别名: FromPairs)
将键/值对的 slice 转换为 map。
```
m := lo.FromEntries([]lo.Entry[string, int]{
{
Key: "foo",
Value: 1,
},
{
Key: "bar",
Value: 2,
},
})
// map[string]int{"foo": 1, "bar": 2}
```
[[play](https://go.dev/play/p/oIr5KHFGCEN)]
### Invert
创建一个由反转的键和值组成的 map。如果 map 包含重复值,则后续值将覆盖先前值的属性分配。
```
m1 := lo.Invert(map[string]int{"a": 1, "b": 2})
// map[int]string{1: "a", 2: "b"}
m2 := lo.Invert(map[string]int{"a": 1, "b": 2, "c": 1})
// map[int]string{1: "c", 2: "b"}
```
[[play](https://go.dev/play/p/rFQ4rak6iA1)]
### Assign
从左到右合并多个 map。
```
mergedMaps := 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}
```
[[play](https://go.dev/play/p/VhwfJOyxf5o)]
### ChunkEntries
将 map 分割成长度等于其大小的元素组的 slice。如果 map 不能被均匀分割,则最后的块将包含剩余的元素。
```
maps := 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},
// }
```
[[play](https://go.dev/play/p/X_YQL6mmoD-)]
### MapKeys
操作 map 键并将其转换为另一种类型的 map。
```
m2 := lo.MapKeys(map[int]int{1: 1, 2: 2, 3: 3, 4: 4}, func(_ int, v int) string {
return strconv.FormatInt(int64(v), 10)
})
// map[string]int{"1": 1, "2": 2, "3": 3, "4": 4}
```
```
// Use MapKeysErr when the iteratee can return an error
m2, err := lo.MapKeysErr(map[int]int{1: 1, 2: 2, 3: 3}, func(_ int, v int) (string, error) {
if v == 2 {
return "", fmt.Errorf("even number not allowed")
}
return strconv.FormatInt(int64(v), 10), nil
})
// map[string]int(nil), error("even number not allowed")
```
[[play](https://go.dev/play/p/9_4WPIqOetJ)]
### MapValues
操作 map 值并将其转换为另一种类型的 map。
```
m1 := map[int]int64{1: 1, 2: 2, 3: 3}
m2 := lo.MapValues(m1, func(x int64, _ int) string {
return strconv.FormatInt(x, 10)
})
// map[int]string{1: "1", 2: "2", 3: "3"}
```
```
// Use MapValuesErr when the iteratee can return an error
m1 := map[int]int64{1: 1, 2: 2, 3: 3}
m2, err := lo.MapValuesErr(m1, func(x int64, _ int) (string, error) {
if x == 2 {
return "", fmt.Errorf("even number not allowed")
}
return strconv.FormatInt(x, 10), nil
})
// map[int]string(nil), error("even number not allowed")
```
[[play](https://go.dev/play/p/T_8xAfvcf0W)]
### MapEntries
操作 map 条目并将其转换为另一种类型的 map。
```
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"}
```
```
// Use MapEntriesErr when the iteratee can return an error
in := map[string]int{"foo": 1, "bar": 2, "baz": 3}
out, err := lo.MapEntriesErr(in, func(k string, v int) (int, string, error) {
if k == "bar" {
return 0, "", fmt.Errorf("bar not allowed")
}
return v, k, nil
})
// map[int]string(nil), error("bar not allowed")
```
[[play](https://go.dev/play/p/VuvNQzxKimT)]
### MapToSlice
基于指定的 iteratee 将 map 转换为 slice。
```
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"}
```
```
// Use MapToSliceErr when the iteratee can return an error
m := map[int]int64{1: 4, 2: 5, 3: 6}
s, err := lo.MapToSliceErr(m, func(k int, v int64) (string, error) {
if k == 2 {
return "", fmt.Errorf("key 2 not allowed")
}
return fmt.Sprintf("%d_%d", k, v), nil
})
// []string(nil), error("key 2 not allowed")
```
[[play](https://go.dev/play/p/ZuiCZpDt6LD)]
### FilterMapToSlice
基于指定的 iteratee 将 map 转换为 slice。iteratee 返回一个值和一个布尔值。如果布尔值为 true,则将该值添加到结果 slice。
如果布尔值为 false,则不将该值添加到结果 slice。输入 map 中键的顺序未指定,并且不保证输出 slice 中键的顺序。
```
kv := map[int]int64{1: 1, 2: 2, 3: 3, 4: 4}
result := lo.FilterMapToSlice(kv, func(k int, v int64) (string, bool) {
return fmt.Sprintf("%d_%d", k, v), k%2 == 0
})
// []{"2_2", "4_4"}
```
```
kv := map[int]int64{1: 1, 2: 2, 3: 3, 4: 4}
result, err := lo.FilterMapToSliceErr(kv, func(k int, v int64) (string, bool, error) {
if k == 3 {
return "", false, fmt.Errorf("key 3 not allowed")
}
return fmt.Sprintf("%d_%d", k, v), k%2 == 0, nil
})
// []string(nil), error("key 3 not allowed")
```
### FilterKeys
基于断言对特定元素返回 true 将 map 转换为 slice。它是 `lo.Filter()` 和 `lo.Keys()` 的混合。
```
kv := map[int]string{1: "foo", 2: "bar", 3: "baz"}
result := FilterKeys(kv, func(k int, v string) bool {
return v == "foo"
})
// [1]
```
```
// Use FilterKeysErr when the predicate can return an error
result, err := lo.FilterKeysErr(map[int]string{1: "foo", 2: "bar", 3: "baz"}, func(k int, v string) (bool, error) {
if k == 3 {
return false, fmt.Errorf("key 3 not allowed")
}
return v == "foo", nil
})
// []int(nil), error("key 3 not allowed")
```
[[play](https://go.dev/play/p/OFlKXlPrBAe)]
### FilterValues
基于断言对特定元素返回 true 将 map 转换为 slice。它是 `lo.Filter()` 和 `lo.Values()` 的混合。
```
kv := map[int]string{1: "foo", 2: "bar", 3: "baz"}
result := FilterValues(kv, func(k int, v string) bool {
return v == "foo"
})
// ["foo"]
```
```
// Use FilterValuesErr when the predicate can return an error
result, err := lo.FilterValuesErr(map[int]string{1: "foo", 2: "bar", 3: "baz"}, func(k int, v string) (bool, error) {
if k == 3 {
return false, fmt.Errorf("key 3 not allowed")
}
return v == "foo", nil
})
// []string(nil), error("key 3 not allowed")
```
[[play](https://go.dev/play/p/YVD5r_h-LX-)]
### Range / RangeFrom / RangeWithSteps
创建一个从 start 开始到(但不包括)end 的数字(正数和/或负数)递增的 slice。
```
result := lo.Range(4)
// [0, 1, 2, 3]
result := lo.Range(-4)
// [0, -1, -2, -3]
result := lo.RangeFrom(1, 5)
// [1, 2, 3, 4, 5]
result := lo.RangeFrom[float64](1.0, 5)
// [1.0, 2.0, 3.0, 4.0, 5.0]
result := lo.RangeWithSteps(0, 20, 5)
// [0, 5, 10, 15]
result := lo.RangeWithSteps[float32](-1.0, -4.0, -1.0)
// [-1.0, -2.0, -3.0]
result := lo.RangeWithSteps(1, 4, -1)
// []
result := lo.Range(0)
// []
```
[[play](https://go.dev/play/p/0r6VimXAi9H)]
### Clamp
将数字限制在包含下限和上限的范围内。
```
r1 := lo.Clamp(0, -10, 10)
// 0
r2 := lo.Clamp(-42, -10, 10)
// -10
r3 := lo.Clamp(42, -10, 10)
// 10
```
[[play](https://go.dev/play/p/RU4lJNC2hlI)]
### Sum
计算集合中值的总和。
如果集合为空,则返回 0。
```
list := []int{1, 2, 3, 4, 5}
sum := lo.Sum(list)
// 15
```
[[play](https://go.dev/play/p/upfeJVqs4Bt)]
### SumBy
使用迭代函数的返回值汇总集合中的值。
如果集合为空,则返回 0。
```
strings := []string{"foo", "bar"}
sum := lo.SumBy(strings, func(item string) int {
return len(item)
})
// 6
```
带错误处理:
```
strings := []string{"foo", "bar", "baz"}
sum, err := lo.SumByErr(strings, func(item string) (int, error) {
if item == "bar" {
return 0, fmt.Errorf("invalid item: %s", item)
}
return len(item), nil
})
// sum: 3, err: invalid item: bar
```
### Product
计算集合中值的乘积。
如果集合为空,则返回 0。
```
list := []int{1, 2, 3, 4, 5}
product := lo.Product(list)
// 120
```
[[play](https://go.dev/play/p/2_kjM_smtAH)]
### ProductBy
使用迭代函数的返回值计算集合中值的乘积。
如果集合为空,则返回 0。
```
strings := []string{"foo", "bar"}
product := lo.ProductBy(strings, func(item string) int {
return len(item)
})
// 9
```
```
// Use ProductByErr when the transform function can return an error
strings := []string{"foo", "bar", "baz"}
product, err := lo.ProductByErr(strings, func(item string) (int, error) {
if item == "bar" {
return 0, fmt.Errorf("bar is not allowed")
}
return len(item), nil
})
// 3, error("bar is not allowed")
```
[[play](https://go.dev/play/p/wadzrWr9Aer)]
### Mean
计算一组数字的平均值。
如果集合为空,则返回 0。
```
mean := lo.Mean([]int{2, 3, 4, 5})
// 3
mean := lo.Mean([]float64{2, 3, 4, 5})
// 3.5
mean := lo.Mean([]float64{})
// 0
```
### MeanBy
使用迭代函数的返回值计算一组数字的平均值。
如果集合为空,则返回 0。
```
list := []string{"aa", "bbb", "cccc", "ddddd"}
mapper := func(item string) float64 {
return float64(len(item))
}
mean := lo.MeanBy(list, mapper)
// 3.5
mean := lo.MeanBy([]float64{}, mapper)
// 0
```
```
// Use MeanByErr when the transform function can return an error
list := []string{"aa", "bbb", "cccc", "ddddd"}
mean, err := lo.MeanByErr(list, func(item string) (float64, error) {
if item == "cccc" {
return 0, fmt.Errorf("cccc is not allowed")
}
return float64(len(item)), nil
})
// 0, error("cccc is not allowed")
```
[[play](https://go.dev/play/p/j7TsVwBOZ7P)]
### Mode
计算一组数字的众数(出现频率最高的值)。
如果多个值具有相同的最高频率,则返回多个值。
如果集合为空,则返回 `T[]` 的零值。
```
mode := lo.Mode([]int{2, 2, 3, 4})
// [2]
mode := lo.Mode([]float64{2, 2, 3, 3})
// [2, 3]
mode := lo.Mode([]float64{})
// []
mode := lo.Mode([]int{1, 2, 3, 4, 5, 6, 7, 8, 9})
// [1, 2, 3, 4, 5, 6, 7, 8, 9]
```
### RandomString
返回指定长度并由指定字符集组成的随机字符串。
```
str := lo.RandomString(5, lo.LettersCharset)
// example: "eIGbt"
```
[[play](https://go.dev/play/p/rRseOQVVum4)]
### Substring
返回字符串的一部分。
```
sub := lo.Substring("hello", 2, 3)
// "llo"
sub := lo.Substring("hello", -4, 3)
// "ell"
sub := lo.Substring("hello", -2, math.MaxUint)
// "lo"
```
[[play](https://go.dev/play/p/TQlxQi82Lu1)]
### ChunkString
返回一个被分割成长度为 size 的组的字符串 slice。如果字符串不能被均匀分割,则最后的块将是剩余的字符。
```
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"}
```
注意:`lo.ChunkString` 和 `lo.Chunk` 函数对于空输入的行为不一致:`lo.ChunkString("", n)` 返回 `[""]` 而不是 `[]`。参见 [#788](https://github.com/samber/lo/issues/788)。
[[play](https://go.dev/play/p/__FLTuJVz54)]
### RuneLength
utf8.RuneCountInString 的别名,它返回字符串中的 rune 数量。
```
sub := lo.RuneLength("hellô")
// 5
sub := len("hellô")
// 6
```
[[play](https://go.dev/play/p/tuhgW_lWY8l)]
### PascalCase
将字符串转换为 PascalCase(大驼峰)。
```
str := lo.PascalCase("hello_world")
// HelloWorld
```
[[play](https://go.dev/play/p/Dy_V_6DUYhe)]
### CamelCase
将字符串转换为 CamelCase(小驼峰)。
```
str := lo.CamelCase("hello_world")
// helloWorld
```
[[play](https://go.dev/play/p/Go6aKwUiq59)]
### KebabCase
将字符串转换为 kebab-case(短横线)。
```
str := lo.KebabCase("helloWorld")
// hello-world
```
[[play](https://go.dev/play/p/96gT_WZnTVP)]
### SnakeCase
将字符串转换为 snake_case(下划线)。
```
str := lo.SnakeCase("HelloWorld")
// hello_world
```
[[play](https://go.dev/play/p/ziB0V89IeVH)]
### Words
将字符串拆分为其单词的 slice。
```
str := lo.Words("helloWorld")
// []string{"hello", "world"}
```
[[play](https://go.dev/play/p/-f3VIQqiaVw)]
### Capitalize
将字符串的第一个字符转换为大写,其余字符转换为小写。
```
str := lo.Capitalize("heLLO")
// Hello
```
[[play](https://go.dev/play/p/uLTZZQXqnsa)]
### Ellipsis
修剪并将字符串截断到指定的 rune 长度(Unicode 码点),如果被截断则追加省略号。多字节字符(如 emoji 或 CJK 象形文字)绝不会从中间分割。
```
str := lo.Ellipsis(" Lorem Ipsum ", 5)
// Lo...
str := lo.Ellipsis("Lorem Ipsum", 100)
// Lorem Ipsum
str := lo.Ellipsis("Lorem Ipsum", 3)
// ...
str := lo.Ellipsis("hello 世界! 你好", 8)
// hello...
str := lo.Ellipsis("🏠🐶🐱🌟", 4)
// 🏠🐶🐱🌟
```
[[play](https://go.dev/play/p/qE93rgqe1TW)]
### T2 -> T9
从值列表创建一个 tuple(元组)。
```
tuple1 := lo.T2("x", 1)
// Tuple2[string, int]{A: "x", B: 1}
func example() (string, int) { return "y", 2 }
tuple2 := lo.T2(example())
// Tuple2[string, int]{A: "y", B: 2}
```
[[play](https://go.dev/play/p/IllL3ZO4BQm)]
### Unpack2 -> Unpack9
返回 tuple 中包含的值。
```
r1, r2 := lo.Unpack2(lo.Tuple2[string, int]{"a", 1})
// "a", 1
```
Unpack 也可以作为 TupleX 的方法使用。
```
tuple2 := lo.T2("a", 1)
a, b := tuple2.Unpack()
// "a", 1
```
[[play](https://go.dev/play/p/xVP_k0kJ96W)]
### Zip2 -> Zip9
Zip 创建一个分组元素的 slice,第一个包含给定 slice 的第一个元素,第二个包含给定 slice 的第二个元素,依此类推。
当集合大小不同时,Tuple 属性将用零值填充。
```
tuples := lo.Zip2([]string{"a", "b"}, []int{1, 2})
// []Tuple2[string, int]{{A: "a", B: 1}, {A: "b", B: 2}}
```
[[play](https://go.dev/play/p/jujaA6GaJTp)]
### ZipBy2 -> ZipBy9
ZipBy 创建一个转换元素的 slice,第一个包含给定 slice 的第一个元素,第二个包含给定 slice 的第二个元素,依此类推。
当集合大小不同时,Tuple 属性将用零值填充。
```
items := lo.ZipBy2([]string{"a", "b"}, []int{1, 2}, func(a string, b int) string {
return fmt.Sprintf("%s-%d", a, b)
})
// []string{"a-1", "b-2"}
```
带错误处理:
```
items, err := lo.ZipByErr2([]string{"a", "b"}, []int{1, 2}, func(a string, b int) (string, error) {
if b == 2 {
return "", fmt.Errorf("number 2 is not allowed")
}
return fmt.Sprintf("%s-%d", a, b), nil
})
// []string(nil), error("number 2 is not allowed")
```
### Unzip2 -> Unzip9
Unzip 接受一个分组元素的 slice,并创建一个将元素重新组合到其预 zip 配置的 slice。
```
a, b := lo.Unzip2([]Tuple2[string, int]{{A: "a", B: 1}, {A: "b", B: 2}})
// []string{"a", "b"}
// []int{1, 2}
```
[[play](https://go.dev/play/p/ciHugugvaAW)]
### UnzipBy2 -> UnzipBy9
UnzipBy2 遍历集合并创建一个 slice,将元素重新组合到其预 zip 配置。
```
a, b := lo.UnzipBy2([]string{"hello", "john", "doe"}, func(str string) (string, int) {
return str, len(str)
})
// []string{"hello", "john", "doe"}
// []int{5, 4, 3}
```
```
a, b, err := lo.UnzipByErr2([]string{"hello", "error", "world"}, func(str string) (string, int, error) {
if str == "error" {
return "", 0, fmt.Errorf("error string not allowed")
}
return str, len(str), nil
})
// []string{}
// []int{}
// error string not allowed
```
### CrossJoin2 -> CrossJoin9
将一个列表中的每个项目与其他列表中的每个项目组合。它是作为参数接收的列表的笛卡尔积。如果列表为空,则返回空列表。
```
result := lo.CrossJoin2([]string{"hello", "john", "doe"}, []int{1, 2})
// lo.Tuple2{"hello", 1}
// lo.Tuple2{"hello", 2}
// lo.Tuple2{"john", 1}
// lo.Tuple2{"john", 2}
// lo.Tuple2{"doe", 1}
// lo.Tuple2{"doe", 2}
```
### CrossJoinBy2 -> CrossJoinBy9
将一个列表中的每个项目与其他列表中的每个项目组合。它是作为参数接收的列表的笛卡尔积。转换函数用于创建输出值。如果列表为空,则返回空列表。
```
result := lo.CrossJoinBy2([]string{"hello", "john", "doe"}, []int{1, 2}, func(a A, b B) string {
return fmt.Sprintf("%s - %d", a, b)
})
// "hello - 1"
// "hello - 2"
// "john - 1"
// "john - 2"
// "doe - 1"
// "doe - 2"
```
带错误处理:
```
result, err := lo.CrossJoinByErr2([]string{"hello", "john"}, []int{1, 2}, func(a string, b int) (string, error) {
if a == "john" {
return "", fmt.Errorf("john not allowed")
}
return fmt.Sprintf("%s - %d", a, b), nil
})
// []string(nil), error("john not allowed")
```
### Duration
返回执行函数所花费的时间。
```
duration := lo.Duration(func() {
// very long job
})
// 3s
```
[[play](https://go.dev/play/p/HQfbBbAXaFP)]
### Duration0 -> Duration10
返回执行函数所花费的时间。
```
duration := lo.Duration0(func() {
// very long job
})
// 3s
err, duration := lo.Duration1(func() error {
// very long job
return errors.New("an error")
})
// an error
// 3s
str, nbr, err, duration := lo.Duration3(func() (string, int, error) {
// very long job
return "hello", 42, nil
})
// hello
// 42
// nil
// 3s
```
### ChannelDispatcher
将消息从输入通道分发到 N 个子通道。关闭事件会传播到子通道。
底层通道可以具有固定的缓冲区容量,或者在 cap 为 0 时为无缓冲。
```
ch := make(chan int, 42)
for i := 0; i <= 10; i++ {
ch <- i
}
children := lo.ChannelDispatcher(ch, 5, 10, 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])
}
```
[[play](https://go.dev/play/p/UZGu2wVg3J2)]
有许多分发策略可用:
- [lo.DispatchingStrategyRoundRobin](./channel.go): 以循环顺序分发消息。
- [lo.DispatchingStrategyRandom](./channel.go): 以随机方式分发消息。
- [lo.DispatchingStrategyWeightedRandom](./channel.go): 以加权方式分发消息。
- [lo.DispatchingStrategyFirst](./channel.go): 在第一个未满的通道中分发消息。
- [lo.DispatchingStrategyLeast](./channel.go): 在最空的通道中分发消息。
- [lo.DispatchingStrategyMost](./channel.go): 分发到最满的通道。
某些策略带有回退机制,以利于非阻塞行为。参见实现。
对于自定义策略,只需 `lo.DispatchingStrategy` 原型:
```
type DispatchingStrategy[T any] func(message T, messageIndex uint64, channels []<-chan T) int
```
例如:
```
type Message struct {
TenantID uuid.UUID
}
func hash(id uuid.UUID) int {
h := fnv.New32a()
h.Write([]byte(id.String()))
return int(h.Sum32())
}
// Routes messages per TenantID.
customStrategy := func(message string, messageIndex uint64, channels []<-chan string) int {
destination := hash(message) % len(channels)
// check if channel is full
if len(channels[destination]) < cap(channels[destination]) {
return destination
}
// fallback when child channel is full
return utils.DispatchingStrategyRoundRobin(message, uint64(destination), channels)
}
children := lo.ChannelDispatcher(ch, 5, 10, customStrategy)
...
```
### SliceToChannel
返回集合元素的只读通道。在最后一个元素之后关闭通道。可以自定义通道容量。
```
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
```
[[play](https://go.dev/play/p/lIbSY3QmiEg)]
### ChannelToSlice
返回由通道项构建的 slice。阻塞直到通道关闭。
```
list := []int{1, 2, 3, 4, 5}
ch := lo.SliceToChannel(2, list)
items := ChannelToSlice(ch)
// []int{1, 2, 3, 4, 5}
```
### 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 个元素的 slice。返回 slice、slice 长度、读取时间和通道状态(打开/关闭)。
```
ch := lo.SliceToChannel(2, []int{1, 2, 3, 4, 5})
items1, length1, duration1, ok1 := lo.Buffer(ch, 3)
// []int{1, 2, 3}, 3, 0s, true
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
}
}
```
### BufferWithContext
从通道创建 n 个元素的 slice,带有超时。返回 slice、slice 长度、读取时间和通道状态(打开/关闭)。
```
ctx, cancel := context.WithCancel(context.TODO())
go func() {
ch <- 0
time.Sleep(10*time.Millisecond)
ch <- 1
time.Sleep(10*time.Millisecond)
ch <- 2
time.Sleep(10*time.Millisecond)
ch <- 3
time.Sleep(10*time.Millisecond)
ch <- 4
time.Sleep(10*time.Millisecond)
cancel()
}()
items1, length1, duration1, ok1 := lo.BufferWithContext(ctx, ch, 3)
// []int{0, 1, 2}, 3, 20ms, true
items2, length2, duration2, ok2 := lo.BufferWithContext(ctx, ch, 3)
// []int{3, 4}, 2, 30ms, false
```
### BufferWithTimeout
从通道创建 n 个元素的 slice,带有超时。返回 slice、slice 长度、读取时间和通道状态(打开/关闭)。
```
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 时,下游通道关闭。
```
stream1 := make(chan int, 42)
stream2 := make(chan int, 42)
stream3 := make(chan int, 42)
all := lo.FanIn(100, stream1, stream2, stream3)
// <-chan int
```
### FanOut
将所有上游消息广播到多个下游通道。当上游通道到达 EOF 时,下游通道关闭。如果任何下游通道已满,则暂停广播。
```
stream := make(chan int, 42)
all := lo.FanOut(5, 100, stream)
// [5]<-chan int
```
### Contains
如果元素存在于集合中,则返回 true。
```
present := lo.Contains([]int{0, 1, 2, 3, 4, 5}, 5)
// true
```
[[play](https://go.dev/play/p/W1EvyqY6t9j)]
### ContainsBy
如果断言函数返回 `true`,则返回 true。
```
present := lo.ContainsBy([]int{0, 1, 2, 3, 4, 5}, func(x int) bool {
return x == 3
})
// true
```
### Every
如果子集的所有元素都包含在集合中,或者子集为空,则返回 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
```
### EveryBy
如果断言对集合中的所有元素都返回 true,或者集合为空,则返回 true。
```
b := EveryBy([]int{1, 2, 3, 4}, func(x int) bool {
return x < 5
})
// true
```
[[play](https://go.dev/play/p/dn1-vhHsq9x)]
### Some
如果子集中至少有 1 个元素包含在集合中,则返回 true。
如果子集为空,Some 返回 false。
```
ok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{0, 6})
// true
```
[[play](https://go.dev/play/p/Lj4ceFkeT9V)]
ok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6})
// false
```
### SomeBy
Returns true if the predicate returns true for any of the elements in the collection.
If the collection is empty SomeBy returns false.
```go
b := SomeBy([]int{1, 2, 3, 4}, func(x int) bool {
return x < 3
})
// true
```
### None
如果子集中没有元素包含在集合中,或者子集为空,则返回 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
```
[[play](https://go.dev/play/p/fye7JsmxzPV)]
### NoneBy
如果断言对集合中的所有元素都不返回 true,或者集合为空,则返回 true。
```
b := NoneBy([]int{1, 2, 3, 4}, func(x int) bool {
return x < 0
})
// true
```
[[play](https://go.dev/play/p/O64WZ32H58S)]
### 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{}
result4 := lo.Intersect([]int{0, 3, 5, 7}, []int{3, 5}, []int{0, 1, 2, 0, 3, 0})
// []int{3}
```
### IntersectBy
使用自定义键选择器函数返回两个集合之间的交集。
```
transform := func(v int) string {
return strconv.Itoa(v)
}
result1 := lo.IntersectBy(transform, []int{0, 1, 2, 3, 4, 5}, []int{0, 2})
// []int{0, 2}
result2 := lo.IntersectBy(transform, []int{0, 1, 2, 3, 4, 5}, []int{0, 6})
// []int{0}
result3 := lo.IntersectBy(transform, []int{0, 1, 2, 3, 4, 5}, []int{-1, 6})
// []int{}
result4 := lo.IntersectBy(transform, []int{0, 3, 5, 7}, []int{3, 5}, []int{0, 1, 2, 0, 3, 0})
// []int{3}
```
### Difference
返回两个集合之间的差异。
- 第一个值是 list2 中不存在的元素集合。
- 第二个值是 list1 中不存在的元素集合。
```
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{}
```
[[play](https://go.dev/play/p/pKE-JgzqRpz)]
### 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}
```
### Without
返回排除所有给定值的 slice。
```
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}
```
### WithoutBy
通过排除其提取键与排除列表中任何键匹配的元素来过滤 slice。
返回一个新 slice,仅包含其键不在排除列表中的元素。
```
type User struct {
ID int
Name string
}
// original users
users := []User{
{ID: 1, Name: "Alice"},
{ID: 2, Name: "Bob"},
{ID: 3, Name: "Charlie"},
}
// extract function to get the user ID
getID := func(user User) int {
return user.ID
}
// exclude users with IDs 2 and 3
excludedIDs := []int{2, 3}
// filtering users
filteredUsers := lo.WithoutBy(users, getID, excludedIDs...)
// []User[{ID: 1, Name: "Alice"}]
```
```
// Use WithoutByErr when the iteratee can return an error
type struct User {
ID int
Name string
}
users := []User{
{ID: 1, Name: "Alice"},
{ID: 2, Name: "Bob"},
{ID: 3, Name: "Charlie"},
}
getID := func(user User) (int, error) {
if user.ID == 2 {
return 0, fmt.Errorf("Bob not allowed")
}
return user.ID, nil
}
filteredUsers, err := lo.WithoutByErr(users, getID, 2, 3)
// []User(nil), error("Bob not allowed")
```
### WithoutEmpty
返回排除零值的 slice。
```
subset := lo.WithoutEmpty([]int{0, 2, 10})
// []int{2, 10}
```
### WithoutNth
返回排除第 n 个值的 slice。
```
subset := lo.WithoutNth([]int{-2, -1, 0, 1, 2}, 3, -42, 1)
// []int{-2, 0, 2}
```
### ElementsMatch
如果列表包含相同的元素集(包括空集),则返回 true。
如果有重复元素,则每个列表中的出现次数应匹配。
不检查元素的顺序。
```
b := lo.ElementsMatch([]int{1, 1, 2}, []int{2, 1, 1})
// true
```
### ElementsMatchBy
如果列表包含相同的元素键集(包括空集),则返回 true。
如果有重复键,则每个列表中的出现次数应匹配。
不检查元素的顺序。
```
b := lo.ElementsMatchBy(
[]someType{a, b},
[]someType{b, a},
func(item someType) string { return item.ID() },
)
// true
```
### IndexOf
返回在 slice 中第一次找到值的索引,如果找不到该值则返回 -1。
```
found := lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 2)
// 2
notFound := lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 6)
// -1
```
[[play](https://go.dev/play/p/Eo7W0lvKTky)]
### LastIndexOf
返回在 slice 中最后一次找到值的索引,如果找不到该值则返回 -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
```
### HasPrefix
如果集合具有前缀,则返回 true。
```
ok := lo.HasPrefix([]int{1, 2, 3, 4}, []int{42})
// false
ok := lo.HasPrefix([]int{1, 2, 3, 4}, []int{1, 2})
// true
```
[[play](https://go.dev/play/p/SrljzVDpMQM)]
### HasSuffix
如果集合具有后缀,则返回 true。
```
ok := lo.HasSuffix([]int{1, 2, 3, 4}, []int{42})
// false
ok := lo.HasSuffix([]int{1, 2, 3, 4}, []int{3, 4})
// true
```
[[play](https://go.dev/play/p/bJeLetQNAON)]
### Find
基于断言在 slice 中搜索元素。如果找到元素,则返回元素和 true。
```
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
```
```
// Use FindErr when the predicate can return an error
str, err := lo.FindErr([]string{"a", "b", "c", "d"}, func(i string) (bool, error) {
if i == "c" {
return false, fmt.Errorf("c is not allowed")
}
return i == "b", nil
})
// "b", nil
str, err = lo.FindErr([]string{"a", "b", "c"}, func(i string) (bool, error) {
if i == "b" {
return false, fmt.Errorf("b is not allowed")
}
return i == "b", nil
})
// "", error("b is not allowed")
```
[[play](https://go.dev/play/p/Eo7W0lvKTky)]
### FindIndexOf
FindIndexOf 基于断言在 slice 中搜索元素,并返回索引和 true。如果未找到元素,则返回 -1 和 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
```
[[play](https://go.dev/play/p/XWSEM4Ic_t0)]
### FindLastIndexOf
FindLastIndexOf 基于断言在 slice 中搜索最后一个元素,并返回索引和 true。如果未找到元素,则返回 -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
```
[[play](https://go.dev/play/p/dPiMRtJ6cUx)]
### FindOrElse
基于断言在 slice 中搜索元素。如果找到则返回该元素,否则返回给定的回退值。
```
str := lo.FindOrElse([]string{"a", "b", "c", "d"}, "x", func(i string) bool {
return i == "b"
})
// "b"
str := lo.FindOrElse([]string{"foobar"}, "x", func(i string) bool {
return i == "b"
})
// "x"
```
### FindKey
返回第一个匹配值的键。
```
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
```
### FindKeyBy
返回断言为 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
```
### FindUniques
返回包含集合中仅出现一次的所有元素的 slice。结果值的顺序由它们在 slice 中出现的顺序决定。
```
uniqueValues := lo.FindUniques([]int{1, 2, 2, 1, 2, 3})
// []int{3}
```
### FindUniquesBy
返回包含集合中仅出现一次的所有元素的 slice。结果值的顺序由它们在 slice 中出现的顺序决定。它接受 `iteratee`,该函数对 slice 中的每个元素调用以生成计算唯一性的标准。
```
uniqueValues := lo.FindUniquesBy([]int{3, 4, 5, 6, 7}, func(i int) int {
return i%3
})
// []int{5}
```
### FindDuplicates
返回包含集合中每个重复元素的第一次出现的 slice。结果值的顺序由它们在 slice 中出现的顺序决定。
```
duplicatedValues := lo.FindDuplicates([]int{1, 2, 2, 1, 2, 3})
// []int{1, 2}
```
### FindDuplicatesBy
返回包含集合中每个重复元素的第一次出现的 slice。结果值的顺序由它们在 slice 中出现的顺序决定。它接受 `iteratee`,该函数对 slice 中的每个元素调用以生成计算唯一性的标准。
```
duplicatedValues := lo.FindDuplicatesBy([]int{3, 4, 5, 6, 7}, func(i int) int {
return i%3
})
// []int{3, 4}
```
带错误处理:
```
duplicatedValues, err := lo.FindDuplicatesByErr([]int{3, 4, 5, 6, 7}, func(i int) (int, error) {
if i == 5 {
return 0, fmt.Errorf("number 5 is not allowed")
}
return i % 3, nil
})
// []int(nil), error("number 5 is not allowed")
```
### Min
搜索集合的最小值。
当集合为空时返回零值。
```
min := lo.Min([]int{1, 2, 3})
// 1
min := lo.Min([]int{})
// 0
min := lo.Min([]time.Duration{time.Second, time.Hour})
// 1s
```
[[play](https://go.dev/play/p/r6e-Z8JozS8)]
### MinIndex
搜索集合的最小值及最小值的索引。
当集合为空时返回(零值, -1)。
```
min, index := lo.MinIndex([]int{1, 2, 3})
// 1, 0
min, index := lo.MinIndex([]int{})
// 0, -1
min, index := lo.MinIndex([]time.Duration{time.Second, time.Hour})
// 1s, 0
```
### MinBy
使用给定的比较函数搜索集合的最小值。
如果集合的多个值等于最小值,则返回第一个这样的值。
当集合为空时返回零值。
```
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)
})
// ""
```
```
// Use MinByErr when the comparison function can return an error
min, err := lo.MinByErr([]string{"s1", "string2", "s3"}, func(item string, min string) (bool, error) {
if item == "string2" {
return false, fmt.Errorf("string2 is not allowed")
}
return len(item) < len(min), nil
})
// "s1", error("string2 is not allowed")
```
### MinIndexBy
使用给定的比较函数搜索集合的最小值及最小值的索引。
如果集合的多个值等于最小值,则返回第一个这样的值。
当集合为空时返回(零值, -1)。
```
min, index := lo.MinIndexBy([]string{"s1", "string2", "s3"}, func(item string, min string) bool {
return len(item) < len(min)
})
// "s1", 0
min, index := lo.MinIndexBy([]string{}, func(item string, min string) bool {
return len(item) < len(min)
})
// "", -1
```
```
min, index, err := lo.MinIndexByErr([]string{"s1", "string2", "s3"}, func(item string, min string) (bool, error) {
if item == "s2" {
return false, fmt.Errorf("s2 is not allowed")
}
return len(item) < len(min), nil
})
// "s1", 0, error("s2 is not allowed")
```
### Earliest
搜索集合的最小 time.Time。
当集合为空时返回零值。
```
earliest := lo.Earliest(time.Now(), time.Time{})
// 0001-01-01 00:00:00 +0000 UTC
```
### EarliestBy
使用给定的 iteratee 函数搜索集合的最小 time.Time。
当集合为空时返回零值。
```
type foo struct {
bar time.Time
}
earliest := lo.EarliestBy([]foo{{time.Now()}, {}}, func(i foo) time.Time {
return i.bar
})
// {bar:{2023-04-01 01:02:03 +0000 UTC}}
```
```
// Use EarliestByErr when the iteratee function can return an error
earliest, err := lo.EarliestByErr([]foo{{time.Now()}, {}}, func(i foo) (time.Time, error) {
if i.bar.IsZero() {
return time.Time{}, fmt.Errorf("zero time not allowed")
}
return i.bar, nil
})
// {bar:{...}}, error("zero time not allowed")
```
### Max
搜索集合的最大值。
当集合为空时返回零值。
```
max := lo.Max([]int{1, 2, 3})
// 3
max := lo.Max([]int{})
// 0
max := lo.Max([]time.Duration{time.Second, time.Hour})
// 1h
```
### MaxIndex
搜索集合的最大值及最大值的索引。
当集合为空时返回(零值, -1)。
```
max, index := lo.MaxIndex([]int{1, 2, 3})
// 3, 2
max, index := lo.MaxIndex([]int{})
// 0, -1
max, index := lo.MaxIndex([]time.Duration{time.Second, time.Hour})
// 1h, 1
```
### MaxBy
使用给定的比较函数搜索集合的最大值。
如果集合的多个值等于最大值,则返回第一个这样的值。
当集合为空时返回零值。
```
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)
})
// ""
```
```
// Use MaxByErr when the comparison function can return an error
max, err := lo.MaxByErr([]string{"string1", "s2", "string3"}, func(item string, max string) (bool, error) {
if item == "s2" {
return false, fmt.Errorf("s2 is not allowed")
}
return len(item) > len(max), nil
})
// "string1", error("s2 is not allowed")
```
[[play](https://go.dev/play/p/JW1qu-ECwF7)]
### MaxIndexBy
使用给定的比较函数搜索集合的最大值及最大值的索引。
如果集合的多个值等于最大值,则返回第一个这样的值。
当集合为空时返回(零值, -1)。
```
max, index := lo.MaxIndexBy([]string{"string1", "s2", "string3"}, func(item string, max string) bool {
return len(item) > len(max)
})
// "string1", 0
max, index := lo.MaxIndexBy([]string{}, func(item string, max string) bool {
return len(item) > len(max)
})
// "", -1
```
```
// Use MaxIndexByErr when the comparison function can return an error
max, index, err := lo.MaxIndexByErr([]string{"string1", "s2", "string3"}, func(item string, max string) (bool, error) {
if item == "s2" {
return false, fmt.Errorf("s2 is not allowed")
}
return len(item) > len(max), nil
})
// "string1", 0, error("s2 is not allowed")
```
[[play](https://go.dev/play/p/uaUszc-c9QK)]
### Latest
搜索集合的最大 time.Time。
当集合为空时返回零值。
```
latest := lo.Latest(time.Now(), time.Time{})
// 2023-04-01 01:02:03 +0000 UTC
```
### LatestBy
使用给定的 iteratee 函数搜索集合的最大 time.Time。
当集合为空时返回零值。
```
type foo struct {
bar time.Time
}
latest := lo.LatestBy([]foo{{time.Now()}, {}}, func(i foo) time.Time {
return i.bar
})
// {bar:{2023-04-01 01:02:03 +0000 UTC}}
```
```
// Use LatestByErr when the iteratee function can return an error
result, err := lo.LatestByErr([]foo{{time.Now()}, {}}, func(i foo) (time.Time, error) {
if i.bar.IsZero() {
return time.Time{}, fmt.Errorf("zero time not allowed")
}
return i.bar, nil
})
// foo{}, error("zero time not allowed")
```
### First
返回集合的第一个元素并检查第一个元素的可用性。
```
first, ok := lo.First([]int{1, 2, 3})
// 1, true
first, ok := lo.First([]int{})
// 0, false
```
### FirstOrEmpty
返回集合的第一个元素,如果为空则返回零值。
```
first := lo.FirstOrEmpty([]int{1, 2, 3})
// 1
first := lo.FirstOrEmpty([]int{})
// 0
```
### FirstOr
返回集合的第一个元素,如果为空则返回回退值。
```
first := lo.FirstOr([]int{1, 2, 3}, 245)
// 1
first := lo.FirstOr([]int{}, 31)
// 31
```
### Last
返回集合的最后一个元素,如果为空则返回错误。
```
last, ok := lo.Last([]int{1, 2, 3})
// 3
// true
last, ok := lo.Last([]int{})
// 0
// false
```
### LastOrEmpty
返回集合的最后一个元素,如果为空则返回零值。
```
last := lo.LastOrEmpty([]int{1, 2, 3})
// 3
last := lo.LastOrEmpty([]int{})
// 0
```
### LastOr
返回集合的最后一个元素,如果为空则返回回退值。
```
last := lo.LastOr([]int{1, 2, 3}, 245)
// 3
last := lo.LastOr([]int{}, 31)
// 31
```
### Nth
返回集合索引为 `nth` 的元素。如果 `nth` 为负数,则返回从末尾开始的第 nth 个元素。当 nth 超出 slice 边界时返回错误。
```
nth, err := lo.Nth([]int{0, 1, 2, 3}, 2)
// 2
nth, err := lo.Nth([]int{0, 1, 2, 3}, -2)
// 2
```
### NthOr
返回集合索引为 `nth` 的元素。如果 `nth` 为负数,则返回从末尾开始的第 nth 个元素。如果 `nth` 超出 slice 边界,则返回提供的回退值。
```
nth := lo.NthOr([]int{10, 20, 30, 40, 50}, 2, -1)
// 30
nth := lo.NthOr([]int{10, 20, 30, 40, 50}, -1, -1)
// 50
nth := lo.NthOr([]int{10, 20, 30, 40, 50}, 5, -1)
// -1 (fallback value)
```
### NthOrEmpty
返回集合索引为 `nth` 的元素。如果 `nth` 为负数,则返回从末尾开始的第 nth 个元素。如果 `nth` 超出 slice 边界,则返回元素类型的零值(例如,整数返回 0,字符串返回 "" 等)。
```
nth := lo.NthOrEmpty([]int{10, 20, 30, 40, 50}, 2)
// 30
nth := lo.NthOrEmpty([]int{10, 20, 30, 40, 50}, -1)
// 50
nth := lo.NthOrEmpty([]int{10, 20, 30, 40, 50}, 5)
// 0 (zero value for int)
nth := lo.NthOrEmpty([]string{"apple", "banana", "cherry"}, 2)
// "cherry"
nth := lo.NthOrEmpty([]string{"apple", "banana", "cherry"}, 5)
// "" (zero value for string)
```
### Sample
从集合中返回一个随机项。
```
lo.Sample([]string{"a", "b", "c"})
// a random string from []string{"a", "b", "c"}
lo.Sample([]string{})
// ""
```
[[play](https://go.dev/play/p/FYA45LcpfM2)]
### SampleBy
使用给定的随机整数生成器从集合中返回一个随机项。
```
import "math/rand"
r := rand.New(rand.NewSource(42))
lo.SampleBy([]string{"a", "b", "c"}, r.Intn)
// a random string from []string{"a", "b", "c"}, using a seeded random generator
lo.SampleBy([]string{}, r.Intn)
// ""
```
### Samples
从集合中返回 N 个随机唯一项。
```
lo.Samples([]string{"a", "b", "c"}, 3)
// []string{"a", "b", "c"} in random order
```
### SamplesBy
使用给定的随机整数生成器从集合中返回 N 个随机唯一项。
```
r := rand.New(rand.NewSource(42))
lo.SamplesBy([]string{"a", "b", "c"}, 3, r.Intn)
// []string{"a", "b", "c"} in random order, using a seeded random generator
```
### Ternary
单行 if/else 语句。
```
result := lo.Ternary(true, "a", "b")
// "a"
result := lo.Ternary(false, "a", "b")
// "b"
```
注意避免在 A/B 表达式中解引用可能为 nil 的指针,因为它们都会被求值。请参阅 TernaryF 以避免此问题。
[[play](https://go.dev/play/p/t-D7WBL44h2)]
### TernaryF
单行 if/else 语句,其选项是函数。
```
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"
```
有助于避免初始化中的 nil 指针解引用,或避免运行不必要的代码。
```
var s *string
someStr := TernaryF(s == nil, func() string { return uuid.New().String() }, func() string { return *s })
// ef782193-c30c-4e2e-a7ae-f8ab5e125e02
```
[[play](https://go.dev/play/p/AO4VW20JoqM)]
### If / ElseIf / Else
```
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
```
[[play](https://go.dev/play/p/WSw3ApMxhyW)]
### Switch / Case / Default
```
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"
```
[[play](https://go.dev/play/p/TGbKUMAeRUd)]
### IsNil
检查值是否为 nil,或者它是否是具有 nil 基础值的引用类型。
```
var x int
lo.IsNil(x)
// false
var k struct{}
lo.IsNil(k)
// false
var i *int
lo.IsNil(i)
// true
var ifaceWithNilValue any = (*string)(nil)
lo.IsNil(ifaceWithNilValue)
// true
ifaceWithNilValue == nil
// false
```
### IsNotNil
检查值是否不为 nil,或者它是否不是具有 nil 基础值的引用类型。
```
var x int
lo.IsNotNil(x)
// true
var k struct{}
lo.IsNotNil(k)
// true
var i *int
lo.IsNotNil(i)
// false
var ifaceWithNilValue any = (*string)(nil)
lo.IsNotNil(ifaceWithNilValue)
// false
ifaceWithNilValue == nil
// true
```
### ToPtr
返回值的指针副本。
```
ptr := lo.ToPtr("hello world")
// *string{"hello world"}
```
[[play](https://go.dev/play/p/P2sD0PMXw4F)]
### Nil
返回类型的 nil 指针。
```
ptr := lo.Nil[float64]()
// nil
```
### EmptyableToPtr
如果值非零,则返回值的指针副本。
否则,返回 nil 指针。
```
ptr := lo.EmptyableToPtr(nil)
// nil
ptr := lo.EmptyableToPtr("")
// nil
ptr := lo.EmptyableToPtr([]int{})
// *[]int{}
ptr := lo.EmptyableToPtr("hello world")
// *string{"hello world"}
```
### FromPtr
返回指针值或空值。
```
str := "hello world"
value := lo.FromPtr(&str)
// "hello world"
value := lo.FromPtr(nil)
// ""
```
### FromPtrOr
返回指针值或回退值。
```
str := "hello world"
value := lo.FromPtrOr(&str, "empty")
// "hello world"
value := lo.FromPtrOr(nil, "empty")
// "empty"
```
### ToSlicePtr
返回指向每个值的指针 slice。
```
ptr := lo.ToSlicePtr([]string{"hello", "world"})
// []*string{"hello", "world"}
```
### FromSlicePtr
返回包含指针值的 slice。
如果遇到 nil 指针元素,则返回零值。
```
str1 := "hello"
str2 := "world"
ptr := lo.FromSlicePtr[string]([]*string{&str1, &str2, nil})
// []string{"hello", "world", ""}
ptr := lo.Compact(
lo.FromSlicePtr[string]([]*string{&str1, &str2, nil}),
)
// []string{"hello", "world"}
```
### FromSlicePtrOr
返回包含指针值或回退值的 slice。
```
str1 := "hello"
str2 := "world"
ptr := lo.FromSlicePtrOr([]*string{&str1, nil, &str2}, "fallback value")
// []string{"hello", "fallback value", "world"}
```
[[play](https://go.dev/play/p/CuXGVzo9G65)]
### ToAnySlice
返回所有元素映射到 `any` 类型的 slice。
```
elements := lo.ToAnySlice([]int{1, 5, 1})
// []any{1, 5, 1}
```
### FromAnySlice
返回所有元素映射到某种类型的 slice。如果类型转换失败则返回 false。
```
elements, ok := lo.FromAnySlice([]any{"foobar", 42})
// []string{}, false
elements, ok := lo.FromAnySlice([]any{"foobar", "42"})
// []string{"foobar", "42"}, true
```
### Empty
返回[零值](https://go.dev/ref/spec#The_zero_value)。
```
lo.Empty[int]()
// 0
lo.Empty[string]()
// ""
lo.Empty[bool]()
// false
```
### IsEmpty
如果参数是零值,则返回 true。
```
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
```
### IsNotEmpty如果参数是零值,则返回 true。
```
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
```
### Coalesce
返回第一个非空参数。参数必须是可比较的。
```
result, ok := lo.Coalesce(0, 1, 2, 3)
// 1 true
result, ok := lo.Coalesce("")
// "" false
var nilStr *string
str := "foobar"
result, ok := lo.Coalesce(nil, nilStr, &str)
// &"foobar" true
```
### CoalesceOrEmpty
返回第一个非空参数。参数必须是可比较的。
```
result := lo.CoalesceOrEmpty(0, 1, 2, 3)
// 1
result := lo.CoalesceOrEmpty("")
// ""
var nilStr *string
str := "foobar"
result := lo.CoalesceOrEmpty(nil, nilStr, &str)
// &"foobar"
```
### CoalesceSlice
返回第一个非零 slice。
```
result, ok := lo.CoalesceSlice([]int{1, 2, 3}, []int{4, 5, 6})
// [1, 2, 3]
// true
result, ok := lo.CoalesceSlice(nil, []int{})
// []
// true
result, ok := lo.CoalesceSlice([]int(nil))
// []
// false
```
### CoalesceSliceOrEmpty
返回第一个非零 slice。
```
result := lo.CoalesceSliceOrEmpty([]int{1, 2, 3}, []int{4, 5, 6})
// [1, 2, 3]
result := lo.CoalesceSliceOrEmpty(nil, []int{})
// []
```
### CoalesceMap
返回第一个非零 map。
```
result, ok := lo.CoalesceMap(map[string]int{"1": 1, "2": 2, "3": 3}, map[string]int{"4": 4, "5": 5, "6": 6})
// {"1": 1, "2": 2, "3": 3}
// true
result, ok := lo.CoalesceMap(nil, map[string]int{})
// {}
// true
result, ok := lo.CoalesceMap(map[string]int(nil))
// {}
// false
```
### CoalesceMapOrEmpty
返回第一个非零 map。
```
result := lo.CoalesceMapOrEmpty(map[string]int{"1": 1, "2": 2, "3": 3}, map[string]int{"4": 4, "5": 5, "6": 6})
// {"1": 1, "2": 2, "3": 3}
result := lo.CoalesceMapOrEmpty(nil, map[string]int{})
// {}
```
### Partial
返回一个新函数,当调用该函数时,其第一个参数设置为提供的值。
```
add := func(x, y int) int { return x + y }
f := lo.Partial(add, 5)
f(10)
// 15
f(42)
// 47
```
[[play](https://go.dev/play/p/Sy1gAQiQZ3v)]
### Partial2 -> Partial5
返回一个新函数,当调用该函数时,其第一个参数设置为提供的值。
```
add := func(x, y, z int) int { return x + y + z }
f := lo.Partial2(add, 42)
f(10, 5)
// 57
f(42, -4)
// 80
```
[[play](https://go.dev/play/p/-xiPjy4JChJ)]
### Attempt
调用函数 N 次直到它返回有效输出。返回捕获的错误或 nil。
当第一个参数小于 `1` 时,函数会一直运行直到返回成功的响应。
```
iter, err := lo.Attempt(42, func(i int) error {
if i == 5 {
return nil
}
return errors.New("failed")
})
// 6
// nil
iter, err := lo.Attempt(2, func(i int) error {
if i == 5 {
return nil
}
return errors.New("failed")
})
// 2
// error "failed"
iter, err := lo.Attempt(0, func(i int) error {
if i < 42 {
return errors.New("failed")
}
return nil
})
// 43
// nil
```
对于更高级的重试策略(延迟、指数退避...),请查看 [cenkalti/backoff](https://github.com/cenkalti/backoff)。
[[play](https://go.dev/play/p/3ggJZ2ZKcMj)]
### AttemptWithDelay
调用函数 N 次直到它返回有效输出,每次调用之间有暂停。返回捕获的错误或 nil。
当第一个参数小于 `1` 时,函数会一直运行直到返回成功的响应。
```
iter, duration, err := lo.AttemptWithDelay(5, 2*time.Second, func(i int, duration time.Duration) error {
if i == 2 {
return nil
}
return errors.New("failed")
})
// 3
// ~ 4 seconds
// nil
```
对于更高级的重试策略(延迟、指数退避...),请查看 [cenkalti/backoff](https://github.com/cenkalti/backoff)。
[[play](https://go.dev/play/p/tVs6CygC7m1)]
### AttemptWhile
调用函数 N 次直到它返回有效输出。返回捕获的错误或 nil,以及一个布尔值来决定是否应再次调用该函数。如果第二个返回值为 false,它将立即终止调用。
当第一个参数小于 `1` 时,函数会一直运行直到返回成功的响应。
```
count1, err1 := lo.AttemptWhile(5, func(i int) (error, bool) {
err := doMockedHTTPRequest(i)
if err != nil {
if errors.Is(err, ErrBadRequest) { // let's 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
})
```
对于更高级的重试策略(延迟、指数退避...),请查看 [cenkalti/backoff](https://github.com/cenkalti/backoff)。
[[play](https://go.dev/play/p/1VS7HxlYMOG)]
### AttemptWhileWithDelay
调用函数 N 次直到它返回有效输出,每次调用之间有暂停。返回捕获的错误或 nil,以及一个布尔值来决定是否应再次调用该函数。如果第二个返回值为 false,它将立即终止调用。
当第一个参数小于 `1` 时,函数会一直运行直到返回成功的响应。
```
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) { // let's 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
})
```
对于更高级的重试策略(延迟、指数退避...),请查看 [cenkalti/backoff](https://github.com/cenkalti/backoff)。
[[play](https://go.dev/play/p/mhufUjJfLEF)]
### Debounce
`NewDebounce` 创建一个去抖动实例,该实例延迟调用给定的函数,直到经过 wait 毫秒后,或者直到调用 `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()
```
[[play](https://go.dev/play/p/mz32VMK2nqe)]
### DebounceBy
`NewDebounceBy` 为每个不同的键创建一个去抖动实例,该实例延迟调用给定的函数,直到经过 wait 毫秒后,或者直到调用 `cancel`。
```
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")
```
[[play](https://go.dev/play/p/d3Vpt6pxhY8)]
### Throttle
创建一个节流实例,在每个时间间隔内仅调用一次给定的函数。
这返回 2 个函数,第一个是节流函数,第二个是重置间隔的函数。
```
f := func() {
println("Called once in every 100ms")
}
throttle, reset := lo.NewThrottle(100 * time.Millisecond, f)
for j := 0; j < 10; j++ {
throttle()
time.Sleep(30 * time.Millisecond)
}
reset()
throttle()
```
`NewThrottleWithCount` 是带有计数限制的 NewThrottle,节流函数在每个时间间隔内会被调用 count 次。
```
f := func() {
println("Called three times in every 100ms")
}
throttle, reset := lo.NewThrottleWithCount(100 * time.Millisecond, f)
for j := 0; j < 10; j++ {
throttle()
time.Sleep(30 * time.Millisecond)
}
reset()
throttle()
```
`NewThrottleBy` 和 `NewThrottleByWithCount` 是带有分片键的 NewThrottle,节流函数在每个时间间隔内会被调用 count 次。
```
f := func(key string) {
println(key, "Called three times in every 100ms")
}
throttle, reset := lo.NewThrottleByWithCount(100 * time.Millisecond, f)
for j := 0; j < 10; j++ {
throttle("foo")
time.Sleep(30 * time.Millisecond)
}
reset()
throttle()
```
### Synchronize
将底层回调包装在 mutex 中。它接收一个可选的 mutex。
```
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
在 goroutine 中执行函数并在通道中返回结果。
```
ch := lo.Async(func() error { time.Sleep(10 * time.Second); return nil })
// chan error (nil)
```
### Async{0->6}
在 goroutine 中执行函数并在通道中返回结果。
对于具有多个返回值的函数,结果将作为 tuple 在通道内返回。
对于没有返回值的函数,通道中将返回 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"})
```
### Transaction
实现 Saga 模式。
```
transaction := NewTransaction().
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, errors.New("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
```
### WaitFor
定期运行直到条件验证通过。
```
alwaysTrue := func(i int) bool { return true }
alwaysFalse := func(i int) bool { return false }
laterTrue := func(i int) bool {
return i > 5
}
iterations, duration, ok := lo.WaitFor(alwaysTrue, 10*time.Millisecond, 2 * time.Millisecond)
// 1
// 1ms
// true
iterations, duration, ok := lo.WaitFor(alwaysFalse, 10*time.Millisecond, time.Millisecond)
// 10
// 10ms
// false
iterations, duration, ok := lo.WaitFor(laterTrue, 10*time.Millisecond, time.Millisecond)
// 7
// 7ms
// true
iterations, duration, ok := lo.WaitFor(laterTrue, 10*time.Millisecond, 5*time.Millisecond)
// 2
// 10ms
// false
```
[[play](https://go.dev/play/p/t_wTDmubbK3)]
### WaitForWithContext
定期运行直到条件验证通过或上下文无效。
条件也接收上下文,因此它可以在条件检查器中使进程无效。
```
ctx := context.Background()
alwaysTrue := func(_ context.Context, i int) bool { return true }
alwaysFalse := func(_ context.Context, i int) bool { return false }
laterTrue := func(_ context.Context, i int) bool {
return i >= 5
}
iterations, duration, ok := lo.WaitForWithContext(ctx, alwaysTrue, 10*time.Millisecond, 2 * time.Millisecond)
// 1
// 1ms
// true
iterations, duration, ok := lo.WaitForWithContext(ctx, alwaysFalse, 10*time.Millisecond, time.Millisecond)
// 10
// 10ms
// false
iterations, duration, ok := lo.WaitForWithContext(ctx, laterTrue, 10*time.Millisecond, time.Millisecond)
// 5
// 5ms
// true
iterations, duration, ok := lo.WaitForWithContext(ctx, laterTrue, 10*time.Millisecond, 5*time.Millisecond)
// 2
// 10ms
// false
expiringCtx, cancel := context.WithTimeout(ctx, 5*time.Millisecond)
iterations, duration, ok := lo.WaitForWithContext(expiringCtx, alwaysFalse, 100*time.Millisecond, time.Millisecond)
// 5
// 5.1ms
// false
```
[[play](https://go.dev/play/p/t_wTDmubbK3)]
### Validate
当条件未满足时创建错误的辅助函数。
```
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
```
[[play](https://go.dev/play/p/vPyh51XpCBt)]
### Must
包装函数调用,如果第二个参数是 `error` 或 `false` 则 panic,否则返回值。
```
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
```
[[play](https://go.dev/play/p/TMoWrRp3DyC)]
### Must{0->6}
Must\* 具有与 Must 相同的行为,但返回多个值。
```
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)` 这样的函数。
```
// math.Signbit(float64) bool
lo.Must0(math.Signbit(v))
// bytes.Cut([]byte,[]byte) ([]byte, []byte, bool)
before, after := lo.Must2(bytes.Cut(s, sep))
```
您可以通过添加一些 printf 类似的参数来为 panic 消息提供上下文。
```
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.Must0(lo.Contains(list, item), "'%s' must always contain '%s'", list, item)
...
```
[[play](https://go.dev/play/p/TMoWrRp3DyC)]
### Try
调用函数并在出错和 panic 时返回 false。
```
ok := lo.Try(func() error {
panic("error")
return nil
})
// false
ok := lo.Try(func() error {
return nil
})
// true
ok := lo.Try(func() error {
return errors.New("error")
})
// false
```
[[play](https://go.dev/play/p/mTyyWUvn9u4)]
### Try{0->6}
与 `Try` 的行为相同,但回调返回 2 个变量。
```
ok := lo.Try2(func() (string, error) {
panic("error")
return "", nil
})
// false
```
[[play](https://go.dev/play/p/mTyyWUvn9u4)]
### TryOr
调用函数并在出错和 panic 时返回默认值。
```
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", errors.New("error")
}, "world")
// world
// false
```
[[play](https://go.dev/play/p/B4F7Wg2Zh9X)]
### TryOr{0->6}
与 `TryOr` 的行为相同,但回调返回 `X` 个变量。
```
str, nbr, ok := lo.TryOr2(func() (string, int, error) {
panic("error")
return "hello", 42, nil
}, "world", 21)
// world
// 21
// false
```
[[play](https://go.dev/play/p/B4F7Wg2Zh9X)]
### TryWithErrorValue
与 `Try` 的行为相同,但也返回传递给 panic 的值。
```
err, ok := lo.TryWithErrorValue(func() error {
panic("error")
return nil
})
// "error", false
```
[[play](https://go.dev/play/p/Kc7afQIT2Fs)]
### TryCatch
与 `Try` 的行为相同,但在出错时调用 catch 函数。
```
caught := false
ok := lo.TryCatch(func() error {
panic("error")
return nil
}, func() {
caught = true
})
// false
// caught == true
```
[[play](https://go.dev/play/p/PnOON-EqBiU)]
### TryCatchWithErrorValue
与 `TryWithErrorValue` 的行为相同,但在出错时调用 catch 函数。
```
caught := false
ok := lo.TryCatchWithErrorValue(func() error {
panic("error")
return nil
}, func(val any) {
caught = val == "error"
})
// false
// caught == true
```
[[play](https://go.dev/play/p/8Pc9gwX_GZO)]
### ErrorsAs
以下代码的快捷方式:
```
err := doSomething()
var rateLimitErr *RateLimitError
if ok := errors.As(err, &rateLimitErr); ok {
// retry later
}
```
单行 `lo` 辅助函数:
```
err := doSomething()
if rateLimitErr, ok := lo.ErrorsAs[*RateLimitError](err); ok {
// retry later
}
```
[[play](https://go.dev/play/p/8wk5rH8UfrE)]
### Assert
当条件为 `true` 时不执行任何操作,否则它会带上可选消息 panic。
在使用它之前请三思,因为 [Go 故意从其标准库中省略了断言](https://go.dev/doc/faq#assertions)。
```
age := getUserAge()
lo.Assert(age >= 15)
```
```
age := getUserAge()
lo.Assert(age >= 15, "user age must be >= 15")
```
[[play](https://go.dev/play/p/Xv8LLKBMNwI)]
### Assertf
类似于 `Assert`,但带有 `fmt.Printf` 风格的格式化。
在使用它之前请三思,因为 [Go 故意从其标准库中省略了断言](https://go.dev/doc/faq#assertions)。
```
age := getUserAge()
lo.Assertf(age >= 15, "user age must be >= 15, got %d", age)
```
[[play](https://go.dev/play/p/TVPEmVcyrdY)]
## 🛩 基准测试
我们用一个极其简单的 `lo.Map` 循环执行了一个简单的基准测试:
在[这里](./map_benchmark_test.go)查看完整实现。
```
_ = lo.Map[int64](arr, func(x int64, i int) string {
return strconv.FormatInt(x, 10)
})
```
**结果:**
这里是 `lo.Map`、`lop.Map`、`go-funk` 库和简单 Go `for` 循环之间的比较。
```
$ go test -benchmem -bench ./...
goos: linux
goarch: amd64
pkg: github.com/samber/lo
cpu: Intel(R) Core(TM) i5-7267U CPU @ 3.10GHz
cpu: Intel(R) Core(TM) i7 CPU 920 @ 2.67GHz
BenchmarkMap/lo.Map-8 8 132728237 ns/op 39998945 B/op 1000002 allocs/op
BenchmarkMap/lop.Map-8 2 503947830 ns/op 119999956 B/op 3000007 allocs/op
BenchmarkMap/reflect-8 2 826400560 ns/op 170326512 B/op 4000042 allocs/op
BenchmarkMap/for-8 9 126252954 ns/op 39998674 B/op 1000001 allocs/op
PASS
ok github.com/samber/lo 6.657s
```
- `lo.Map` 比 `go-funk`(一个基于反射的 Map 实现)快得多 (x7)。
- `lo.Map` 具有与 `for` 相同的分配配置文件。
- `lo.Map` 比 `for` 慢 4%。
- `lop.Map` 比 `lo.Map` 慢,因为它意味着更多的内存分配和锁。`lop.Map` 对于长时间运行的回调很有用,例如 I/O 密集型处理。
- `for` 在内存和 CPU 方面击败了其他实现。
## 🤝 贡献
- 在 Twitter [@samuelberthe](https://twitter.com/samuelberthe) 上联系我(私信、提及,随便 :))
- Fork 这个[项目](https://github.com/samber/lo)
- 修复[未解决的问题](https://github.com/samber/lo/issues)或请求新功能
不要犹豫 ;)
辅助函数命名:辅助函数必须不言自明并遵守标准(其他语言、库...)。欢迎在您的贡献中提出许多名称建议。
```
# 安装一些开发依赖
make tools
# 运行测试
make test
# 或
make watch-test
```
## 👤 贡献者

## 💫 表示您的支持
如果这个项目对您有帮助,请给一个 ⭐️!
[](https://github.com/sponsors/samber)
## 📝 许可证
版权所有 © 2022 [Samuel Berthe](https://github.com/samber)。
本项目采用 [MIT](./LICENSE) 许可证。
DBOS - Durable workflow orchestration library for Go
标签:EVTX分析, EVTX分析, Generics, Go, Golang, Lodash, Map/Reduce, Ruby工具, samber/lo, 代码辅助, 函数式编程, 切片处理, 字符串处理, 安全编程, 工具库, 开发效率, 开源库, 搜索引擎爬虫, 数据结构, 日志审计, 泛型, 网络可观测性