samber/lo

GitHub: samber/lo

基于 Go 1.18+ 泛型的 Lodash 风格工具库,提供丰富的 slice、map、channel 及并发操作函数

Stars: 21063 | Forks: 937

# lo - 迭代 slice、map、channel... [![tag](https://img.shields.io/github/tag/samber/lo.svg)](https://github.com/samber/lo/releases) ![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.18-%23007d9c) [![GoDoc](https://godoc.org/github.com/samber/lo?status.svg)](https://pkg.go.dev/github.com/samber/lo) ![Build Status](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/208fcfa079142226.svg) [![Go report](https://goreportcard.com/badge/github.com/samber/lo)](https://goreportcard.com/report/github.com/samber/lo) [![Coverage](https://img.shields.io/codecov/c/github/samber/lo)](https://codecov.io/gh/samber/lo) [![Contributors](https://img.shields.io/github/contributors/samber/lo)](https://github.com/samber/lo/graphs/contributors) [![License](https://img.shields.io/github/license/samber/lo)](./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:
dbos
DBOS - Durable workflow orchestration library for Go
**为什么叫这个名字?** 我想要一个**简短的名字**,类似于 "Lodash",而且没有 Go 包使用这个名字。 ![lo](https://static.pigsec.cn/wp-content/uploads/repos/2026/03/fd4b061994142227.png) ## 🚀 安装 ``` 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 ``` ## 👤 贡献者 ![Contributors](https://contrib.rocks/image?repo=samber/lo) ## 💫 表示您的支持 如果这个项目对您有帮助,请给一个 ⭐️! [![GitHub Sponsors](https://img.shields.io/github/sponsors/samber?style=for-the-badge)](https://github.com/sponsors/samber) ## 📝 许可证 版权所有 © 2022 [Samuel Berthe](https://github.com/samber)。 本项目采用 [MIT](./LICENSE) 许可证。
标签:EVTX分析, EVTX分析, Generics, Go, Golang, Lodash, Map/Reduce, Ruby工具, samber/lo, 代码辅助, 函数式编程, 切片处理, 字符串处理, 安全编程, 工具库, 开发效率, 开源库, 搜索引擎爬虫, 数据结构, 日志审计, 泛型, 网络可观测性