{"id":13416675,"url":"https://github.com/samber/lo","last_synced_at":"2025-05-12T17:46:46.822Z","repository":{"id":37386434,"uuid":"465303307","full_name":"samber/lo","owner":"samber","description":"💥  A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...)","archived":false,"fork":false,"pushed_at":"2025-05-05T13:16:08.000Z","size":2788,"stargazers_count":19337,"open_issues_count":205,"forks_count":871,"subscribers_count":83,"default_branch":"master","last_synced_at":"2025-05-05T15:11:29.924Z","etag":null,"topics":["constraints","contract","filterable","foldable","functional","generics","go","golang","lodash","programming","typesafe"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/samber/lo","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/samber.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["samber"]}},"created_at":"2022-03-02T12:48:45.000Z","updated_at":"2025-05-05T13:58:45.000Z","dependencies_parsed_at":"2024-04-28T03:30:09.390Z","dependency_job_id":"bcbab176-e477-4e80-b3b2-c7849cfd0094","html_url":"https://github.com/samber/lo","commit_stats":{"total_commits":421,"total_committers":123,"mean_commits":"3.4227642276422765","dds":"0.49643705463182897","last_synced_commit":"5044a1d9ab160c5892104e87d5d882b912d968fc"},"previous_names":[],"tags_count":61,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samber%2Flo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samber%2Flo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samber%2Flo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/samber%2Flo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/samber","download_url":"https://codeload.github.com/samber/lo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253789899,"owners_count":21964769,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["constraints","contract","filterable","foldable","functional","generics","go","golang","lodash","programming","typesafe"],"created_at":"2024-07-30T22:00:19.405Z","updated_at":"2025-05-12T17:46:45.648Z","avatar_url":"https://github.com/samber.png","language":"Go","funding_links":["https://github.com/sponsors/samber"],"categories":["Popular","Utilities","Go","开源类库","Overview","Open source library","🤖 AI \u0026 Machine Learning","公用事业公司","语言资源库","Repos","常用工具","programming","\u003ca name=\"Go\"\u003e\u003c/a\u003eGo"],"sub_categories":["Utility/Miscellaneous","开发辅助包","Why?","Fail injection","Development Aid Package","实用程序/Miscellaneous","go","爬虫工具"],"readme":"\n# lo - Iterate over slices, maps, channels...\n\n[![tag](https://img.shields.io/github/tag/samber/lo.svg)](https://github.com/samber/lo/releases)\n![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.18-%23007d9c)\n[![GoDoc](https://godoc.org/github.com/samber/lo?status.svg)](https://pkg.go.dev/github.com/samber/lo)\n![Build Status](https://github.com/samber/lo/actions/workflows/test.yml/badge.svg)\n[![Go report](https://goreportcard.com/badge/github.com/samber/lo)](https://goreportcard.com/report/github.com/samber/lo)\n[![Coverage](https://img.shields.io/codecov/c/github/samber/lo)](https://codecov.io/gh/samber/lo)\n[![Contributors](https://img.shields.io/github/contributors/samber/lo)](https://github.com/samber/lo/graphs/contributors)\n[![License](https://img.shields.io/github/license/samber/lo)](./LICENSE)\n\n✨ **`samber/lo` is a Lodash-style Go library based on Go 1.18+ Generics.**\n\nA utility library based on Go 1.18+ generics that makes it easier to work with slices, maps, strings, channels, and functions. It provides dozens of handy methods to simplify common coding tasks and improve code readability. It may look like [Lodash](https://github.com/lodash/lodash) in some aspects.\n\n5 to 10 helpers may overlap with those from the Go standard library, in packages `slices` and `maps`. I feel this library is legitimate and offers many more valuable abstractions.\n\n**See also:**\n\n- [samber/do](https://github.com/samber/do): A dependency injection toolkit based on Go 1.18+ Generics\n- [samber/mo](https://github.com/samber/mo): Monads based on Go 1.18+ Generics (Option, Result, Either...)\n\n**Why this name?**\n\nI wanted a **short name**, similar to \"Lodash\", and no Go package uses this name.\n\n![lo](img/logo-full.png)\n\n## 🚀 Install\n\n```sh\ngo get github.com/samber/lo@v1\n```\n\nThis library is v1 and follows SemVer strictly.\n\nNo breaking changes will be made to exported APIs before v2.0.0.\n\nThis library has no dependencies outside the Go standard library.\n\n## 💡 Usage\n\nYou can import `lo` using:\n\n```go\nimport (\n    \"github.com/samber/lo\"\n    lop \"github.com/samber/lo/parallel\"\n    lom \"github.com/samber/lo/mutable\"\n)\n```\n\nThen use one of the helpers below:\n\n```go\nnames := lo.Uniq([]string{\"Samuel\", \"John\", \"Samuel\"})\n// []string{\"Samuel\", \"John\"}\n```\n\n### Tips for lazy developers\n\nI cannot recommend it, but in case you are too lazy for repeating `lo.` everywhere, you can import the entire library into the namespace.\n\n```go\nimport (\n    . \"github.com/samber/lo\"\n)\n```\n\nI take no responsibility on this junk. 😁 💩\n\n## 🤠 Spec\n\nGoDoc: [https://godoc.org/github.com/samber/lo](https://godoc.org/github.com/samber/lo)\n\nSupported helpers for slices:\n\n- [Filter](#filter)\n- [Map](#map)\n- [UniqMap](#uniqmap)\n- [FilterMap](#filtermap)\n- [FlatMap](#flatmap)\n- [Reduce](#reduce)\n- [ReduceRight](#reduceright)\n- [ForEach](#foreach)\n- [ForEachWhile](#foreachwhile)\n- [Times](#times)\n- [Uniq](#uniq)\n- [UniqBy](#uniqby)\n- [GroupBy](#groupby)\n- [GroupByMap](#groupbymap)\n- [Chunk](#chunk)\n- [PartitionBy](#partitionby)\n- [Flatten](#flatten)\n- [Interleave](#interleave)\n- [Shuffle](#shuffle)\n- [Reverse](#reverse)\n- [Fill](#fill)\n- [Repeat](#repeat)\n- [RepeatBy](#repeatby)\n- [KeyBy](#keyby)\n- [SliceToMap / Associate](#slicetomap-alias-associate)\n- [FilterSliceToMap](#filterslicetomap)\n- [Keyify](#keyify)\n- [Drop](#drop)\n- [DropRight](#dropright)\n- [DropWhile](#dropwhile)\n- [DropRightWhile](#droprightwhile)\n- [DropByIndex](#DropByIndex)\n- [Reject](#reject)\n- [RejectMap](#rejectmap)\n- [FilterReject](#filterreject)\n- [Count](#count)\n- [CountBy](#countby)\n- [CountValues](#countvalues)\n- [CountValuesBy](#countvaluesby)\n- [Subset](#subset)\n- [Slice](#slice)\n- [Replace](#replace)\n- [ReplaceAll](#replaceall)\n- [Compact](#compact)\n- [IsSorted](#issorted)\n- [IsSortedByKey](#issortedbykey)\n- [Splice](#Splice)\n\nSupported helpers for maps:\n\n- [Keys](#keys)\n- [UniqKeys](#uniqkeys)\n- [HasKey](#haskey)\n- [ValueOr](#valueor)\n- [Values](#values)\n- [UniqValues](#uniqvalues)\n- [PickBy](#pickby)\n- [PickByKeys](#pickbykeys)\n- [PickByValues](#pickbyvalues)\n- [OmitBy](#omitby)\n- [OmitByKeys](#omitbykeys)\n- [OmitByValues](#omitbyvalues)\n- [Entries / ToPairs](#entries-alias-topairs)\n- [FromEntries / FromPairs](#fromentries-alias-frompairs)\n- [Invert](#invert)\n- [Assign (merge of maps)](#assign)\n- [MapKeys](#mapkeys)\n- [MapValues](#mapvalues)\n- [MapEntries](#mapentries)\n- [MapToSlice](#maptoslice)\n- [FilterMapToSlice](#FilterMapToSlice)\n\nSupported math helpers:\n\n- [Range / RangeFrom / RangeWithSteps](#range--rangefrom--rangewithsteps)\n- [Clamp](#clamp)\n- [Sum](#sum)\n- [SumBy](#sumby)\n- [Product](#product)\n- [ProductBy](#productby)\n- [Mean](#mean)\n- [MeanBy](#meanby)\n\nSupported helpers for strings:\n\n- [RandomString](#randomstring)\n- [Substring](#substring)\n- [ChunkString](#chunkstring)\n- [RuneLength](#runelength)\n- [PascalCase](#pascalcase)\n- [CamelCase](#camelcase)\n- [KebabCase](#kebabcase)\n- [SnakeCase](#snakecase)\n- [Words](#words)\n- [Capitalize](#capitalize)\n- [Ellipsis](#ellipsis)\n\nSupported helpers for tuples:\n\n- [T2 -\u003e T9](#t2---t9)\n- [Unpack2 -\u003e Unpack9](#unpack2---unpack9)\n- [Zip2 -\u003e Zip9](#zip2---zip9)\n- [ZipBy2 -\u003e ZipBy9](#zipby2---zipby9)\n- [Unzip2 -\u003e Unzip9](#unzip2---unzip9)\n- [UnzipBy2 -\u003e UnzipBy9](#unzipby2---unzipby9)\n- [CrossJoin2 -\u003e CrossJoin2](#crossjoin2---crossjoin9)\n- [CrossJoinBy2 -\u003e CrossJoinBy2](#crossjoinby2---crossjoinby9)\n\nSupported helpers for time and duration:\n\n- [Duration](#duration)\n- [Duration0 -\u003e Duration10](#duration0---duration10)\n\nSupported helpers for channels:\n\n- [ChannelDispatcher](#channeldispatcher)\n- [SliceToChannel](#slicetochannel)\n- [Generator](#generator)\n- [Buffer](#buffer)\n- [BufferWithContext](#bufferwithcontext)\n- [BufferWithTimeout](#bufferwithtimeout)\n- [FanIn](#fanin)\n- [FanOut](#fanout)\n\nSupported intersection helpers:\n\n- [Contains](#contains)\n- [ContainsBy](#containsby)\n- [Every](#every)\n- [EveryBy](#everyby)\n- [Some](#some)\n- [SomeBy](#someby)\n- [None](#none)\n- [NoneBy](#noneby)\n- [Intersect](#intersect)\n- [Difference](#difference)\n- [Union](#union)\n- [Without](#without)\n- [WithoutBy](#withoutby)\n- [WithoutEmpty](#withoutempty)\n- [WithoutNth](#withoutnth)\n- [ElementsMatch](#ElementsMatch)\n- [ElementsMatchBy](#ElementsMatchBy)\n\nSupported search helpers:\n\n- [IndexOf](#indexof)\n- [LastIndexOf](#lastindexof)\n- [Find](#find)\n- [FindIndexOf](#findindexof)\n- [FindLastIndexOf](#findlastindexof)\n- [FindOrElse](#findorelse)\n- [FindKey](#findkey)\n- [FindKeyBy](#findkeyby)\n- [FindUniques](#finduniques)\n- [FindUniquesBy](#finduniquesby)\n- [FindDuplicates](#findduplicates)\n- [FindDuplicatesBy](#findduplicatesby)\n- [Min](#min)\n- [MinIndex](#minindex)\n- [MinBy](#minby)\n- [MinIndexBy](#minindexby)\n- [Earliest](#earliest)\n- [EarliestBy](#earliestby)\n- [Max](#max)\n- [MaxIndex](#maxindex)\n- [MaxBy](#maxby)\n- [MaxIndexBy](#maxindexby)\n- [Latest](#latest)\n- [LatestBy](#latestby)\n- [First](#first)\n- [FirstOrEmpty](#FirstOrEmpty)\n- [FirstOr](#FirstOr)\n- [Last](#last)\n- [LastOrEmpty](#LastOrEmpty)\n- [LastOr](#LastOr)\n- [Nth](#nth)\n- [NthOr](#nthor)\n- [NthOrEmpty](#nthorempty)\n- [Sample](#sample)\n- [SampleBy](#sampleby)\n- [Samples](#samples)\n- [SamplesBy](#samplesby)\n\nConditional helpers:\n\n- [Ternary](#ternary)\n- [TernaryF](#ternaryf)\n- [If / ElseIf / Else](#if--elseif--else)\n- [Switch / Case / Default](#switch--case--default)\n\nType manipulation helpers:\n\n- [IsNil](#isnil)\n- [IsNotNil](#isnotnil)\n- [ToPtr](#toptr)\n- [Nil](#nil)\n- [EmptyableToPtr](#emptyabletoptr)\n- [FromPtr](#fromptr)\n- [FromPtrOr](#fromptror)\n- [ToSlicePtr](#tosliceptr)\n- [FromSlicePtr](#fromsliceptr)\n- [FromSlicePtrOr](#fromsliceptror)\n- [ToAnySlice](#toanyslice)\n- [FromAnySlice](#fromanyslice)\n- [Empty](#empty)\n- [IsEmpty](#isempty)\n- [IsNotEmpty](#isnotempty)\n- [Coalesce](#coalesce)\n- [CoalesceOrEmpty](#coalesceorempty)\n- [CoalesceSlice](#coalesceslice)\n- [CoalesceSliceOrEmpty](#coalescesliceorempty)\n- [CoalesceMap](#coalescemap)\n- [CoalesceMapOrEmpty](#coalescemaporempty)\n\nFunction helpers:\n\n- [Partial](#partial)\n- [Partial2 -\u003e Partial5](#partial2---partial5)\n\nConcurrency helpers:\n\n- [Attempt](#attempt)\n- [AttemptWhile](#attemptwhile)\n- [AttemptWithDelay](#attemptwithdelay)\n- [AttemptWhileWithDelay](#attemptwhilewithdelay)\n- [Debounce](#debounce)\n- [DebounceBy](#debounceby)\n- [Throttle](#throttle)\n- [ThrottleWithCount](#throttle)\n- [ThrottleBy](#throttle)\n- [ThrottleByWithCount](#throttle)\n- [Synchronize](#synchronize)\n- [Async](#async)\n- [Transaction](#transaction)\n- [WaitFor](#waitfor)\n- [WaitForWithContext](#waitforwithcontext)\n\nError handling:\n\n- [Validate](#validate)\n- [Must](#must)\n- [Try](#try)\n- [Try1 -\u003e Try6](#try0-6)\n- [TryOr](#tryor)\n- [TryOr1 -\u003e TryOr6](#tryor0-6)\n- [TryCatch](#trycatch)\n- [TryWithErrorValue](#trywitherrorvalue)\n- [TryCatchWithErrorValue](#trycatchwitherrorvalue)\n- [ErrorsAs](#errorsas)\n\nConstraints:\n\n- Clonable\n\n### Filter\n\nIterates over a collection and returns an array of all the elements the predicate function returns `true` for.\n\n```go\neven := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {\n    return x%2 == 0\n})\n// []int{2, 4}\n```\n\n[[play](https://go.dev/play/p/Apjg3WeSi7K)]\n\nMutable: like `lo.Filter()`, but the slice is updated in place.\n\n```go\nimport lom \"github.com/samber/lo/mutable\"\n\nlist := []int{1, 2, 3, 4}\nnewList := lom.Filter(list, func(x int) bool {\n    return x%2 == 0\n})\n\nlist\n// []int{2, 4, 3, 4}\n\nnewList\n// []int{2, 4}\n```\n\n### Map\n\nManipulates a slice of one type and transforms it into a slice of another type:\n\n```go\nimport \"github.com/samber/lo\"\n\nlo.Map([]int64{1, 2, 3, 4}, func(x int64, index int) string {\n    return strconv.FormatInt(x, 10)\n})\n// []string{\"1\", \"2\", \"3\", \"4\"}\n```\n\n[[play](https://go.dev/play/p/OkPcYAhBo0D)]\n\nParallel processing: like `lo.Map()`, but the mapper function is called in a goroutine. Results are returned in the same order.\n\n```go\nimport lop \"github.com/samber/lo/parallel\"\n\nlop.Map([]int64{1, 2, 3, 4}, func(x int64, _ int) string {\n    return strconv.FormatInt(x, 10)\n})\n// []string{\"1\", \"2\", \"3\", \"4\"}\n```\n\nMutable: like `lo.Map()`, but the slice is updated in place.\n\n```go\nimport lom \"github.com/samber/lo/mutable\"\n\nlist := []int{1, 2, 3, 4}\nlom.Map(list, func(x int) int {\n    return i*2\n})\n// []int{2, 4, 6, 8}\n```\n\n### UniqMap\n\nManipulates a slice and transforms it to a slice of another type with unique values.\n\n```go\ntype User struct {\n    Name string\n    Age  int\n}\nusers := []User{{Name: \"Alex\", Age: 10}, {Name: \"Alex\", Age: 12}, {Name: \"Bob\", Age: 11}, {Name: \"Alice\", Age: 20}}\n\nnames := lo.UniqMap(users, func(u User, index int) string {\n    return u.Name\n})\n// []string{\"Alex\", \"Bob\", \"Alice\"}\n```\n\n### FilterMap\n\nReturns a slice which obtained after both filtering and mapping using the given callback function.\n\nThe callback function should return two values: the result of the mapping operation and whether the result element should be included or not.\n\n```go\nmatching := lo.FilterMap([]string{\"cpu\", \"gpu\", \"mouse\", \"keyboard\"}, func(x string, _ int) (string, bool) {\n    if strings.HasSuffix(x, \"pu\") {\n        return \"xpu\", true\n    }\n    return \"\", false\n})\n// []string{\"xpu\", \"xpu\"}\n```\n\n[[play](https://go.dev/play/p/-AuYXfy7opz)]\n\n### FlatMap\n\nManipulates a slice and transforms and flattens it to a slice of another type. The transform function can either return a slice or a `nil`, and in the `nil` case no value is added to the final slice.\n\n```go\nlo.FlatMap([]int64{0, 1, 2}, func(x int64, _ int) []string {\n    return []string{\n        strconv.FormatInt(x, 10),\n        strconv.FormatInt(x, 10),\n    }\n})\n// []string{\"0\", \"0\", \"1\", \"1\", \"2\", \"2\"}\n```\n\n[[play](https://go.dev/play/p/YSoYmQTA8-U)]\n\n### Reduce\n\nReduces a collection to a single value. The value is calculated by accumulating the result of running each element in the collection through an accumulator function. Each successive invocation is supplied with the return value returned by the previous call.\n\n```go\nsum := lo.Reduce([]int{1, 2, 3, 4}, func(agg int, item int, _ int) int {\n    return agg + item\n}, 0)\n// 10\n```\n\n[[play](https://go.dev/play/p/R4UHXZNaaUG)]\n\n### ReduceRight\n\nLike `lo.Reduce` except that it iterates over elements of collection from right to left.\n\n```go\nresult := lo.ReduceRight([][]int{{0, 1}, {2, 3}, {4, 5}}, func(agg []int, item []int, _ int) []int {\n    return append(agg, item...)\n}, []int{})\n// []int{4, 5, 2, 3, 0, 1}\n```\n\n[[play](https://go.dev/play/p/Fq3W70l7wXF)]\n\n### ForEach\n\nIterates over elements of a collection and invokes the function over each element.\n\n```go\nimport \"github.com/samber/lo\"\n\nlo.ForEach([]string{\"hello\", \"world\"}, func(x string, _ int) {\n    println(x)\n})\n// prints \"hello\\nworld\\n\"\n```\n\n[[play](https://go.dev/play/p/oofyiUPRf8t)]\n\nParallel processing: like `lo.ForEach()`, but the callback is called as a goroutine.\n\n```go\nimport lop \"github.com/samber/lo/parallel\"\n\nlop.ForEach([]string{\"hello\", \"world\"}, func(x string, _ int) {\n    println(x)\n})\n// prints \"hello\\nworld\\n\" or \"world\\nhello\\n\"\n```\n\n### ForEachWhile\n\nIterates over collection elements and invokes iteratee for each element collection return value decide to continue or break, like do while().\n\n```go\nlist := []int64{1, 2, -42, 4}\n\nlo.ForEachWhile(list, func(x int64, _ int) bool {\n\tif x \u003c 0 {\n\t\treturn false\n\t}\n\tfmt.Println(x)\n\treturn true\n})\n// 1\n// 2\n```\n\n[[play](https://go.dev/play/p/QnLGt35tnow)]\n\n### Times\n\nTimes invokes the iteratee n times, returning an array of the results of each invocation. The iteratee is invoked with index as argument.\n\n```go\nimport \"github.com/samber/lo\"\n\nlo.Times(3, func(i int) string {\n    return strconv.FormatInt(int64(i), 10)\n})\n// []string{\"0\", \"1\", \"2\"}\n```\n\n[[play](https://go.dev/play/p/vgQj3Glr6lT)]\n\nParallel processing: like `lo.Times()`, but callback is called in goroutine.\n\n```go\nimport lop \"github.com/samber/lo/parallel\"\n\nlop.Times(3, func(i int) string {\n    return strconv.FormatInt(int64(i), 10)\n})\n// []string{\"0\", \"1\", \"2\"}\n```\n\n### Uniq\n\nReturns a duplicate-free version of an array, in which only the first occurrence of each element is kept. The order of result values is determined by the order they occur in the array.\n\n```go\nuniqValues := lo.Uniq([]int{1, 2, 2, 1})\n// []int{1, 2}\n```\n\n[[play](https://go.dev/play/p/DTzbeXZ6iEN)]\n\n### UniqBy\n\nReturns a duplicate-free version of an array, in which only the first occurrence of each element is kept. The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is invoked for each element in array to generate the criterion by which uniqueness is computed.\n\n```go\nuniqValues := lo.UniqBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {\n    return i%3\n})\n// []int{0, 1, 2}\n```\n\n[[play](https://go.dev/play/p/g42Z3QSb53u)]\n\n### GroupBy\n\nReturns an object composed of keys generated from the results of running each element of collection through iteratee.\n\n```go\nimport lo \"github.com/samber/lo\"\n\ngroups := lo.GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {\n    return i%3\n})\n// map[int][]int{0: []int{0, 3}, 1: []int{1, 4}, 2: []int{2, 5}}\n```\n\n[[play](https://go.dev/play/p/XnQBd_v6brd)]\n\nParallel processing: like `lo.GroupBy()`, but callback is called in goroutine.\n\n```go\nimport lop \"github.com/samber/lo/parallel\"\n\nlop.GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {\n    return i%3\n})\n// map[int][]int{0: []int{0, 3}, 1: []int{1, 4}, 2: []int{2, 5}}\n```\n\n### GroupByMap\n\nReturns an object composed of keys generated from the results of running each element of collection through iteratee.\n\n```go\nimport lo \"github.com/samber/lo\"\n\ngroups := lo.GroupByMap([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, int) {\n    return i%3, i*2\n})\n// map[int][]int{0: []int{0, 6}, 1: []int{2, 8}, 2: []int{4, 10}}\n```\n\n### Chunk\n\nReturns an array of elements split into groups the length of size. If array can't be split evenly, the final chunk will be the remaining elements.\n\n```go\nlo.Chunk([]int{0, 1, 2, 3, 4, 5}, 2)\n// [][]int{{0, 1}, {2, 3}, {4, 5}}\n\nlo.Chunk([]int{0, 1, 2, 3, 4, 5, 6}, 2)\n// [][]int{{0, 1}, {2, 3}, {4, 5}, {6}}\n\nlo.Chunk([]int{}, 2)\n// [][]int{}\n\nlo.Chunk([]int{0}, 2)\n// [][]int{{0}}\n```\n\n[[play](https://go.dev/play/p/EeKl0AuTehH)]\n\n### PartitionBy\n\nReturns an array of elements split into groups. The order of grouped values is determined by the order they occur in collection. The grouping is generated from the results of running each element of collection through iteratee.\n\n```go\nimport lo \"github.com/samber/lo\"\n\npartitions := lo.PartitionBy([]int{-2, -1, 0, 1, 2, 3, 4, 5}, func(x int) string {\n    if x \u003c 0 {\n        return \"negative\"\n    } else if x%2 == 0 {\n        return \"even\"\n    }\n    return \"odd\"\n})\n// [][]int{{-2, -1}, {0, 2, 4}, {1, 3, 5}}\n```\n\n[[play](https://go.dev/play/p/NfQ_nGjkgXW)]\n\nParallel processing: like `lo.PartitionBy()`, but callback is called in goroutine. Results are returned in the same order.\n\n```go\nimport lop \"github.com/samber/lo/parallel\"\n\npartitions := lop.PartitionBy([]int{-2, -1, 0, 1, 2, 3, 4, 5}, func(x int) string {\n    if x \u003c 0 {\n        return \"negative\"\n    } else if x%2 == 0 {\n        return \"even\"\n    }\n    return \"odd\"\n})\n// [][]int{{-2, -1}, {0, 2, 4}, {1, 3, 5}}\n```\n\n### Flatten\n\nReturns an array a single level deep.\n\n```go\nflat := lo.Flatten([][]int{{0, 1}, {2, 3, 4, 5}})\n// []int{0, 1, 2, 3, 4, 5}\n```\n\n[[play](https://go.dev/play/p/rbp9ORaMpjw)]\n\n### Interleave\n\nRound-robin alternating input slices and sequentially appending value at index into result.\n\n```go\ninterleaved := lo.Interleave([]int{1, 4, 7}, []int{2, 5, 8}, []int{3, 6, 9})\n// []int{1, 2, 3, 4, 5, 6, 7, 8, 9}\n\ninterleaved := lo.Interleave([]int{1}, []int{2, 5, 8}, []int{3, 6}, []int{4, 7, 9, 10})\n// []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}\n```\n\n[[play](https://go.dev/play/p/-RJkTLQEDVt)]\n\n### Shuffle\n\nReturns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm.\n\n⚠️ This helper is **mutable**.\n\n```go\nimport lom \"github.com/samber/lo/mutable\"\n\nlist := []int{0, 1, 2, 3, 4, 5}\nlom.Shuffle(list)\n\nlist\n// []int{1, 4, 0, 3, 5, 2}\n```\n\n[[play](https://go.dev/play/p/2xb3WdLjeSJ)]\n\n### Reverse\n\nReverses array so that the first element becomes the last, the second element becomes the second to last, and so on.\n\n⚠️ This helper is **mutable**.\n\n```go\nimport lom \"github.com/samber/lo/mutable\"\n\nlist := []int{0, 1, 2, 3, 4, 5}\nlom.Reverse(list)\n\nlist\n// []int{5, 4, 3, 2, 1, 0}\n```\n\n[[play](https://go.dev/play/p/O-M5pmCRgzV)]\n\n### Fill\n\nFills elements of array with `initial` value.\n\n```go\ntype foo struct {\n  bar string\n}\n\nfunc (f foo) Clone() foo {\n  return foo{f.bar}\n}\n\ninitializedSlice := lo.Fill([]foo{foo{\"a\"}, foo{\"a\"}}, foo{\"b\"})\n// []foo{foo{\"b\"}, foo{\"b\"}}\n```\n\n[[play](https://go.dev/play/p/VwR34GzqEub)]\n\n### Repeat\n\nBuilds a slice with N copies of initial value.\n\n```go\ntype foo struct {\n  bar string\n}\n\nfunc (f foo) Clone() foo {\n  return foo{f.bar}\n}\n\nslice := lo.Repeat(2, foo{\"a\"})\n// []foo{foo{\"a\"}, foo{\"a\"}}\n```\n\n[[play](https://go.dev/play/p/g3uHXbmc3b6)]\n\n### RepeatBy\n\nBuilds a slice with values returned by N calls of callback.\n\n```go\nslice := lo.RepeatBy(0, func (i int) string {\n    return strconv.FormatInt(int64(math.Pow(float64(i), 2)), 10)\n})\n// []string{}\n\nslice := lo.RepeatBy(5, func(i int) string {\n    return strconv.FormatInt(int64(math.Pow(float64(i), 2)), 10)\n})\n// []string{\"0\", \"1\", \"4\", \"9\", \"16\"}\n```\n\n[[play](https://go.dev/play/p/ozZLCtX_hNU)]\n\n### KeyBy\n\nTransforms a slice or an array of structs to a map based on a pivot callback.\n\n```go\nm := lo.KeyBy([]string{\"a\", \"aa\", \"aaa\"}, func(str string) int {\n    return len(str)\n})\n// map[int]string{1: \"a\", 2: \"aa\", 3: \"aaa\"}\n\ntype Character struct {\n  dir  string\n  code int\n}\ncharacters := []Character{\n    {dir: \"left\", code: 97},\n    {dir: \"right\", code: 100},\n}\nresult := lo.KeyBy(characters, func(char Character) string {\n    return string(rune(char.code))\n})\n//map[a:{dir:left code:97} d:{dir:right code:100}]\n```\n\n[[play](https://go.dev/play/p/mdaClUAT-zZ)]\n\n### SliceToMap (alias: Associate)\n\nReturns a map containing key-value pairs provided by transform function applied to elements of the given slice.\nIf any of two pairs would have the same key the last one gets added to the map.\n\nThe order of keys in returned map is not specified and is not guaranteed to be the same from the original array.\n\n```go\nin := []*foo{{baz: \"apple\", bar: 1}, {baz: \"banana\", bar: 2}}\n\naMap := lo.SliceToMap(in, func (f *foo) (string, int) {\n    return f.baz, f.bar\n})\n// map[string][int]{ \"apple\":1, \"banana\":2 }\n```\n\n[[play](https://go.dev/play/p/WHa2CfMO3Lr)]\n\n### FilterSliceToMap\n\nReturns a map containing key-value pairs provided by transform function applied to elements of the given slice.\n\nIf any of two pairs would have the same key the last one gets added to the map.\n\nThe order of keys in returned map is not specified and is not guaranteed to be the same from the original array.\n\nThe third return value of the transform function is a boolean that indicates whether the key-value pair should be included in the map.\n\n\n```go\nlist := []string{\"a\", \"aa\", \"aaa\"}\n\nresult := lo.FilterSliceToMap(list, func(str string) (string, int, bool) {\n    return str, len(str), len(str) \u003e 1\n})\n// map[string][int]{\"aa\":2 \"aaa\":3}\n```\n\n### Keyify\n\nReturns a map with each unique element of the slice as a key.\n\n```go\nset := lo.Keyify([]int{1, 1, 2, 3, 4})\n// map[int]struct{}{1:{}, 2:{}, 3:{}, 4:{}}\n```\n\n### Drop\n\nDrops n elements from the beginning of a slice or array.\n\n```go\nl := lo.Drop([]int{0, 1, 2, 3, 4, 5}, 2)\n// []int{2, 3, 4, 5}\n```\n\n[[play](https://go.dev/play/p/JswS7vXRJP2)]\n\n### DropRight\n\nDrops n elements from the end of a slice or array.\n\n```go\nl := lo.DropRight([]int{0, 1, 2, 3, 4, 5}, 2)\n// []int{0, 1, 2, 3}\n```\n\n[[play](https://go.dev/play/p/GG0nXkSJJa3)]\n\n### DropWhile\n\nDrop elements from the beginning of a slice or array while the predicate returns true.\n\n```go\nl := lo.DropWhile([]string{\"a\", \"aa\", \"aaa\", \"aa\", \"aa\"}, func(val string) bool {\n    return len(val) \u003c= 2\n})\n// []string{\"aaa\", \"aa\", \"aa\"}\n```\n\n[[play](https://go.dev/play/p/7gBPYw2IK16)]\n\n### DropRightWhile\n\nDrop elements from the end of a slice or array while the predicate returns true.\n\n```go\nl := lo.DropRightWhile([]string{\"a\", \"aa\", \"aaa\", \"aa\", \"aa\"}, func(val string) bool {\n    return len(val) \u003c= 2\n})\n// []string{\"a\", \"aa\", \"aaa\"}\n```\n\n[[play](https://go.dev/play/p/3-n71oEC0Hz)]\n\n### DropByIndex\n\nDrops elements from a slice or array by the index. A negative index will drop elements from the end of the slice.\n\n```go\nl := lo.DropByIndex([]int{0, 1, 2, 3, 4, 5}, 2, 4, -1)\n// []int{0, 1, 3}\n```\n\n[[play](https://go.dev/play/p/JswS7vXRJP2)]\n\n### Reject\n\nThe opposite of Filter, this method returns the elements of collection that predicate does not return truthy for.\n\n```go\nodd := lo.Reject([]int{1, 2, 3, 4}, func(x int, _ int) bool {\n    return x%2 == 0\n})\n// []int{1, 3}\n```\n\n[[play](https://go.dev/play/p/YkLMODy1WEL)]\n\n### RejectMap\n\nThe opposite of FilterMap, this method returns a slice which obtained after both filtering and mapping using the given callback function.\n\nThe callback function should return two values:\n\n- the result of the mapping operation and\n- whether the result element should be included or not.\n\n```go\nitems := lo.RejectMap([]int{1, 2, 3, 4}, func(x int, _ int) (int, bool) {\n    return x*10, x%2 == 0\n})\n// []int{10, 30}\n```\n\n### FilterReject\n\nMixes Filter and Reject, this method returns two slices, one for the elements of collection that predicate returns truthy for and one for the elements that predicate does not return truthy for.\n\n```go\nkept, rejected := lo.FilterReject([]int{1, 2, 3, 4}, func(x int, _ int) bool {\n    return x%2 == 0\n})\n// []int{2, 4}\n// []int{1, 3}\n```\n\n### Count\n\nCounts the number of elements in the collection that compare equal to value.\n\n```go\ncount := lo.Count([]int{1, 5, 1}, 1)\n// 2\n```\n\n[[play](https://go.dev/play/p/Y3FlK54yveC)]\n\n### CountBy\n\nCounts the number of elements in the collection for which predicate is true.\n\n```go\ncount := lo.CountBy([]int{1, 5, 1}, func(i int) bool {\n    return i \u003c 4\n})\n// 2\n```\n\n[[play](https://go.dev/play/p/ByQbNYQQi4X)]\n\n### CountValues\n\nCounts the number of each element in the collection.\n\n```go\nlo.CountValues([]int{})\n// map[int]int{}\n\nlo.CountValues([]int{1, 2})\n// map[int]int{1: 1, 2: 1}\n\nlo.CountValues([]int{1, 2, 2})\n// map[int]int{1: 1, 2: 2}\n\nlo.CountValues([]string{\"foo\", \"bar\", \"\"})\n// map[string]int{\"\": 1, \"foo\": 1, \"bar\": 1}\n\nlo.CountValues([]string{\"foo\", \"bar\", \"bar\"})\n// map[string]int{\"foo\": 1, \"bar\": 2}\n```\n\n[[play](https://go.dev/play/p/-p-PyLT4dfy)]\n\n### CountValuesBy\n\nCounts the number of each element in the collection. It ss equivalent to chaining lo.Map and lo.CountValues.\n\n```go\nisEven := func(v int) bool {\n    return v%2==0\n}\n\nlo.CountValuesBy([]int{}, isEven)\n// map[bool]int{}\n\nlo.CountValuesBy([]int{1, 2}, isEven)\n// map[bool]int{false: 1, true: 1}\n\nlo.CountValuesBy([]int{1, 2, 2}, isEven)\n// map[bool]int{false: 1, true: 2}\n\nlength := func(v string) int {\n    return len(v)\n}\n\nlo.CountValuesBy([]string{\"foo\", \"bar\", \"\"}, length)\n// map[int]int{0: 1, 3: 2}\n\nlo.CountValuesBy([]string{\"foo\", \"bar\", \"bar\"}, length)\n// map[int]int{3: 3}\n```\n\n[[play](https://go.dev/play/p/2U0dG1SnOmS)]\n\n### Subset\n\nReturns a copy of a slice from `offset` up to `length` elements. Like `slice[start:start+length]`, but does not panic on overflow.\n\n```go\nin := []int{0, 1, 2, 3, 4}\n\nsub := lo.Subset(in, 2, 3)\n// []int{2, 3, 4}\n\nsub := lo.Subset(in, -4, 3)\n// []int{1, 2, 3}\n\nsub := lo.Subset(in, -2, math.MaxUint)\n// []int{3, 4}\n```\n\n[[play](https://go.dev/play/p/tOQu1GhFcog)]\n\n### Slice\n\nReturns a copy of a slice from `start` up to, but not including `end`. Like `slice[start:end]`, but does not panic on overflow.\n\n```go\nin := []int{0, 1, 2, 3, 4}\n\nslice := lo.Slice(in, 0, 5)\n// []int{0, 1, 2, 3, 4}\n\nslice := lo.Slice(in, 2, 3)\n// []int{2}\n\nslice := lo.Slice(in, 2, 6)\n// []int{2, 3, 4}\n\nslice := lo.Slice(in, 4, 3)\n// []int{}\n```\n\n[[play](https://go.dev/play/p/8XWYhfMMA1h)]\n\n### Replace\n\nReturns a copy of the slice with the first n non-overlapping instances of old replaced by new.\n\n```go\nin := []int{0, 1, 0, 1, 2, 3, 0}\n\nslice := lo.Replace(in, 0, 42, 1)\n// []int{42, 1, 0, 1, 2, 3, 0}\n\nslice := lo.Replace(in, -1, 42, 1)\n// []int{0, 1, 0, 1, 2, 3, 0}\n\nslice := lo.Replace(in, 0, 42, 2)\n// []int{42, 1, 42, 1, 2, 3, 0}\n\nslice := lo.Replace(in, 0, 42, -1)\n// []int{42, 1, 42, 1, 2, 3, 42}\n```\n\n[[play](https://go.dev/play/p/XfPzmf9gql6)]\n\n### ReplaceAll\n\nReturns a copy of the slice with all non-overlapping instances of old replaced by new.\n\n```go\nin := []int{0, 1, 0, 1, 2, 3, 0}\n\nslice := lo.ReplaceAll(in, 0, 42)\n// []int{42, 1, 42, 1, 2, 3, 42}\n\nslice := lo.ReplaceAll(in, -1, 42)\n// []int{0, 1, 0, 1, 2, 3, 0}\n```\n\n[[play](https://go.dev/play/p/a9xZFUHfYcV)]\n\n### Compact\n\nReturns a slice of all non-zero elements.\n\n```go\nin := []string{\"\", \"foo\", \"\", \"bar\", \"\"}\n\nslice := lo.Compact(in)\n// []string{\"foo\", \"bar\"}\n```\n\n[[play](https://go.dev/play/p/tXiy-iK6PAc)]\n\n### IsSorted\n\nChecks if a slice is sorted.\n\n```go\nslice := lo.IsSorted([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})\n// true\n```\n\n[[play](https://go.dev/play/p/mc3qR-t4mcx)]\n\n### IsSortedByKey\n\nChecks if a slice is sorted by iteratee.\n\n```go\nslice := lo.IsSortedByKey([]string{\"a\", \"bb\", \"ccc\"}, func(s string) int {\n    return len(s)\n})\n// true\n```\n\n[[play](https://go.dev/play/p/wiG6XyBBu49)]\n\n### Splice\n\nSplice inserts multiple elements at index i. A negative index counts back from the end of the slice. The helper is protected against overflow errors.\n\n```go\nresult := lo.Splice([]string{\"a\", \"b\"}, 1, \"1\", \"2\")\n// []string{\"a\", \"1\", \"2\", \"b\"}\n\n// negative\nresult = lo.Splice([]string{\"a\", \"b\"}, -1, \"1\", \"2\")\n// []string{\"a\", \"1\", \"2\", \"b\"}\n\n// overflow\nresult = lo.Splice([]string{\"a\", \"b\"}, 42, \"1\", \"2\")\n// []string{\"a\", \"b\", \"1\", \"2\"}\n```\n\n[[play](https://go.dev/play/p/wiG6XyBBu49)]\n\n### Keys\n\nCreates a slice of the map keys.\n\nUse the UniqKeys variant to deduplicate common keys.\n\n```go\nkeys := lo.Keys(map[string]int{\"foo\": 1, \"bar\": 2})\n// []string{\"foo\", \"bar\"}\n\nkeys := lo.Keys(map[string]int{\"foo\": 1, \"bar\": 2}, map[string]int{\"baz\": 3})\n// []string{\"foo\", \"bar\", \"baz\"}\n\nkeys := lo.Keys(map[string]int{\"foo\": 1, \"bar\": 2}, map[string]int{\"bar\": 3})\n// []string{\"foo\", \"bar\", \"bar\"}\n```\n\n[[play](https://go.dev/play/p/Uu11fHASqrU)]\n\n### UniqKeys\n\nCreates an array of unique map keys.\n\n```go\nkeys := lo.UniqKeys(map[string]int{\"foo\": 1, \"bar\": 2}, map[string]int{\"baz\": 3})\n// []string{\"foo\", \"bar\", \"baz\"}\n\nkeys := lo.UniqKeys(map[string]int{\"foo\": 1, \"bar\": 2}, map[string]int{\"bar\": 3})\n// []string{\"foo\", \"bar\"}\n```\n\n[[play](https://go.dev/play/p/TPKAb6ILdHk)]\n\n### HasKey\n\nReturns whether the given key exists.\n\n```go\nexists := lo.HasKey(map[string]int{\"foo\": 1, \"bar\": 2}, \"foo\")\n// true\n\nexists := lo.HasKey(map[string]int{\"foo\": 1, \"bar\": 2}, \"baz\")\n// false\n```\n\n[[play](https://go.dev/play/p/aVwubIvECqS)]\n\n### Values\n\nCreates an array of the map values.\n\nUse the UniqValues variant to deduplicate common values.\n\n```go\nvalues := lo.Values(map[string]int{\"foo\": 1, \"bar\": 2})\n// []int{1, 2}\n\nvalues := lo.Values(map[string]int{\"foo\": 1, \"bar\": 2}, map[string]int{\"baz\": 3})\n// []int{1, 2, 3}\n\nvalues := lo.Values(map[string]int{\"foo\": 1, \"bar\": 2}, map[string]int{\"bar\": 2})\n// []int{1, 2, 2}\n```\n\n[[play](https://go.dev/play/p/nnRTQkzQfF6)]\n\n### UniqValues\n\nCreates an array of unique map values.\n\n```go\nvalues := lo.UniqValues(map[string]int{\"foo\": 1, \"bar\": 2})\n// []int{1, 2}\n\nvalues := lo.UniqValues(map[string]int{\"foo\": 1, \"bar\": 2}, map[string]int{\"baz\": 3})\n// []int{1, 2, 3}\n\nvalues := lo.UniqValues(map[string]int{\"foo\": 1, \"bar\": 2}, map[string]int{\"bar\": 2})\n// []int{1, 2}\n```\n\n[[play](https://go.dev/play/p/nf6bXMh7rM3)]\n\n### ValueOr\n\nReturns the value of the given key or the fallback value if the key is not present.\n\n```go\nvalue := lo.ValueOr(map[string]int{\"foo\": 1, \"bar\": 2}, \"foo\", 42)\n// 1\n\nvalue := lo.ValueOr(map[string]int{\"foo\": 1, \"bar\": 2}, \"baz\", 42)\n// 42\n```\n\n[[play](https://go.dev/play/p/bAq9mHErB4V)]\n\n### PickBy\n\nReturns same map type filtered by given predicate.\n\n```go\nm := lo.PickBy(map[string]int{\"foo\": 1, \"bar\": 2, \"baz\": 3}, func(key string, value int) bool {\n    return value%2 == 1\n})\n// map[string]int{\"foo\": 1, \"baz\": 3}\n```\n\n[[play](https://go.dev/play/p/kdg8GR_QMmf)]\n\n### PickByKeys\n\nReturns same map type filtered by given keys.\n\n```go\nm := lo.PickByKeys(map[string]int{\"foo\": 1, \"bar\": 2, \"baz\": 3}, []string{\"foo\", \"baz\"})\n// map[string]int{\"foo\": 1, \"baz\": 3}\n```\n\n[[play](https://go.dev/play/p/R1imbuci9qU)]\n\n### PickByValues\n\nReturns same map type filtered by given values.\n\n```go\nm := lo.PickByValues(map[string]int{\"foo\": 1, \"bar\": 2, \"baz\": 3}, []int{1, 3})\n// map[string]int{\"foo\": 1, \"baz\": 3}\n```\n\n[[play](https://go.dev/play/p/1zdzSvbfsJc)]\n\n### OmitBy\n\nReturns same map type filtered by given predicate.\n\n```go\nm := lo.OmitBy(map[string]int{\"foo\": 1, \"bar\": 2, \"baz\": 3}, func(key string, value int) bool {\n    return value%2 == 1\n})\n// map[string]int{\"bar\": 2}\n```\n\n[[play](https://go.dev/play/p/EtBsR43bdsd)]\n\n### OmitByKeys\n\nReturns same map type filtered by given keys.\n\n```go\nm := lo.OmitByKeys(map[string]int{\"foo\": 1, \"bar\": 2, \"baz\": 3}, []string{\"foo\", \"baz\"})\n// map[string]int{\"bar\": 2}\n```\n\n[[play](https://go.dev/play/p/t1QjCrs-ysk)]\n\n### OmitByValues\n\nReturns same map type filtered by given values.\n\n```go\nm := lo.OmitByValues(map[string]int{\"foo\": 1, \"bar\": 2, \"baz\": 3}, []int{1, 3})\n// map[string]int{\"bar\": 2}\n```\n\n[[play](https://go.dev/play/p/9UYZi-hrs8j)]\n\n### Entries (alias: ToPairs)\n\nTransforms a map into array of key/value pairs.\n\n```go\nentries := lo.Entries(map[string]int{\"foo\": 1, \"bar\": 2})\n// []lo.Entry[string, int]{\n//     {\n//         Key: \"foo\",\n//         Value: 1,\n//     },\n//     {\n//         Key: \"bar\",\n//         Value: 2,\n//     },\n// }\n```\n\n[[play](https://go.dev/play/p/3Dhgx46gawJ)]\n\n### FromEntries (alias: FromPairs)\n\nTransforms an array of key/value pairs into a map.\n\n```go\nm := lo.FromEntries([]lo.Entry[string, int]{\n    {\n        Key: \"foo\",\n        Value: 1,\n    },\n    {\n        Key: \"bar\",\n        Value: 2,\n    },\n})\n// map[string]int{\"foo\": 1, \"bar\": 2}\n```\n\n[[play](https://go.dev/play/p/oIr5KHFGCEN)]\n\n### Invert\n\nCreates a map composed of the inverted keys and values. If map contains duplicate values, subsequent values overwrite property assignments of previous values.\n\n```go\nm1 := lo.Invert(map[string]int{\"a\": 1, \"b\": 2})\n// map[int]string{1: \"a\", 2: \"b\"}\n\nm2 := lo.Invert(map[string]int{\"a\": 1, \"b\": 2, \"c\": 1})\n// map[int]string{1: \"c\", 2: \"b\"}\n```\n\n[[play](https://go.dev/play/p/rFQ4rak6iA1)]\n\n### Assign\n\nMerges multiple maps from left to right.\n\n```go\nmergedMaps := lo.Assign(\n    map[string]int{\"a\": 1, \"b\": 2},\n    map[string]int{\"b\": 3, \"c\": 4},\n)\n// map[string]int{\"a\": 1, \"b\": 3, \"c\": 4}\n```\n\n[[play](https://go.dev/play/p/VhwfJOyxf5o)]\n\n### ChunkEntries\n\nSplits a map into an array of elements in groups of a length equal to its size. If the map cannot be split evenly, the final chunk will contain the remaining elements.\n\n```go\nmaps := lo.ChunkEntries(\n    map[string]int{\n        \"a\": 1,\n        \"b\": 2,\n        \"c\": 3,\n        \"d\": 4,\n        \"e\": 5,\n    },\n    3,\n)\n// []map[string]int{\n//    {\"a\": 1, \"b\": 2, \"c\": 3},\n//    {\"d\": 4, \"e\": 5},\n// }\n```\n[[play](https://go.dev/play/p/X_YQL6mmoD-)]\n\n### MapKeys\n\nManipulates a map keys and transforms it to a map of another type.\n\n```go\nm2 := lo.MapKeys(map[int]int{1: 1, 2: 2, 3: 3, 4: 4}, func(_ int, v int) string {\n    return strconv.FormatInt(int64(v), 10)\n})\n// map[string]int{\"1\": 1, \"2\": 2, \"3\": 3, \"4\": 4}\n```\n\n[[play](https://go.dev/play/p/9_4WPIqOetJ)]\n\n### MapValues\n\nManipulates a map values and transforms it to a map of another type.\n\n```go\nm1 := map[int]int64{1: 1, 2: 2, 3: 3}\n\nm2 := lo.MapValues(m1, func(x int64, _ int) string {\n    return strconv.FormatInt(x, 10)\n})\n// map[int]string{1: \"1\", 2: \"2\", 3: \"3\"}\n```\n\n[[play](https://go.dev/play/p/T_8xAfvcf0W)]\n\n### MapEntries\n\nManipulates a map entries and transforms it to a map of another type.\n\n```go\nin := map[string]int{\"foo\": 1, \"bar\": 2}\n\nout := lo.MapEntries(in, func(k string, v int) (int, string) {\n    return v,k\n})\n// map[int]string{1: \"foo\", 2: \"bar\"}\n```\n\n[[play](https://go.dev/play/p/VuvNQzxKimT)]\n\n### MapToSlice\n\nTransforms a map into a slice based on specific iteratee.\n\n```go\nm := map[int]int64{1: 4, 2: 5, 3: 6}\n\ns := lo.MapToSlice(m, func(k int, v int64) string {\n    return fmt.Sprintf(\"%d_%d\", k, v)\n})\n// []string{\"1_4\", \"2_5\", \"3_6\"}\n```\n\n[[play](https://go.dev/play/p/ZuiCZpDt6LD)]\n\n### FilterMapToSlice\n\nTransforms a map into a slice based on specific iteratee. The iteratee returns a value and a boolean. If the boolean is true, the value is added to the result slice.\n\nIf the boolean is false, the value is not added to the result slice. The order of the keys in the input map is not specified and the order of the keys in the output slice is not guaranteed.\n\n```go\nkv := map[int]int64{1: 1, 2: 2, 3: 3, 4: 4}\n\nresult := lo.FilterMapToSlice(kv, func(k int, v int64) (string, bool) {\n    return fmt.Sprintf(\"%d_%d\", k, v), k%2 == 0\n})\n// []{\"2_2\", \"4_4\"}\n```\n\n### Range / RangeFrom / RangeWithSteps\n\nCreates an array of numbers (positive and/or negative) progressing from start up to, but not including end.\n\n```go\nresult := lo.Range(4)\n// [0, 1, 2, 3]\n\nresult := lo.Range(-4)\n// [0, -1, -2, -3]\n\nresult := lo.RangeFrom(1, 5)\n// [1, 2, 3, 4, 5]\n\nresult := lo.RangeFrom[float64](1.0, 5)\n// [1.0, 2.0, 3.0, 4.0, 5.0]\n\nresult := lo.RangeWithSteps(0, 20, 5)\n// [0, 5, 10, 15]\n\nresult := lo.RangeWithSteps[float32](-1.0, -4.0, -1.0)\n// [-1.0, -2.0, -3.0]\n\nresult := lo.RangeWithSteps(1, 4, -1)\n// []\n\nresult := lo.Range(0)\n// []\n```\n\n[[play](https://go.dev/play/p/0r6VimXAi9H)]\n\n### Clamp\n\nClamps number within the inclusive lower and upper bounds.\n\n```go\nr1 := lo.Clamp(0, -10, 10)\n// 0\n\nr2 := lo.Clamp(-42, -10, 10)\n// -10\n\nr3 := lo.Clamp(42, -10, 10)\n// 10\n```\n\n[[play](https://go.dev/play/p/RU4lJNC2hlI)]\n\n### Sum\n\nSums the values in a collection.\n\nIf collection is empty 0 is returned.\n\n```go\nlist := []int{1, 2, 3, 4, 5}\nsum := lo.Sum(list)\n// 15\n```\n\n[[play](https://go.dev/play/p/upfeJVqs4Bt)]\n\n### SumBy\n\nSummarizes the values in a collection using the given return value from the iteration function.\n\nIf collection is empty 0 is returned.\n\n```go\nstrings := []string{\"foo\", \"bar\"}\nsum := lo.SumBy(strings, func(item string) int {\n    return len(item)\n})\n// 6\n```\n\n### Product\n\nCalculates the product of the values in a collection.\n\nIf collection is empty 0 is returned.\n\n```go\nlist := []int{1, 2, 3, 4, 5}\nproduct := lo.Product(list)\n// 120\n```\n\n[[play](https://go.dev/play/p/2_kjM_smtAH)]\n\n### ProductBy\n\nCalculates the product of the values in a collection using the given return value from the iteration function.\n\nIf collection is empty 0 is returned.\n\n```go\nstrings := []string{\"foo\", \"bar\"}\nproduct := lo.ProductBy(strings, func(item string) int {\n    return len(item)\n})\n// 9\n```\n\n[[play](https://go.dev/play/p/wadzrWr9Aer)]\n\n### Mean\n\nCalculates the mean of a collection of numbers.\n\nIf collection is empty 0 is returned.\n\n```go\nmean := lo.Mean([]int{2, 3, 4, 5})\n// 3\n\nmean := lo.Mean([]float64{2, 3, 4, 5})\n// 3.5\n\nmean := lo.Mean([]float64{})\n// 0\n```\n\n### MeanBy\n\nCalculates the mean of a collection of numbers using the given return value from the iteration function.\n\nIf collection is empty 0 is returned.\n\n```go\nlist := []string{\"aa\", \"bbb\", \"cccc\", \"ddddd\"}\nmapper := func(item string) float64 {\n    return float64(len(item))\n}\n\nmean := lo.MeanBy(list, mapper)\n// 3.5\n\nmean := lo.MeanBy([]float64{}, mapper)\n// 0\n```\n\n### RandomString\n\nReturns a random string of the specified length and made of the specified charset.\n\n```go\nstr := lo.RandomString(5, lo.LettersCharset)\n// example: \"eIGbt\"\n```\n\n[[play](https://go.dev/play/p/rRseOQVVum4)]\n\n### Substring\n\nReturn part of a string.\n\n```go\nsub := lo.Substring(\"hello\", 2, 3)\n// \"llo\"\n\nsub := lo.Substring(\"hello\", -4, 3)\n// \"ell\"\n\nsub := lo.Substring(\"hello\", -2, math.MaxUint)\n// \"lo\"\n```\n\n[[play](https://go.dev/play/p/TQlxQi82Lu1)]\n\n### ChunkString\n\nReturns an array of strings split into groups the length of size. If array can't be split evenly, the final chunk will be the remaining elements.\n\n```go\nlo.ChunkString(\"123456\", 2)\n// []string{\"12\", \"34\", \"56\"}\n\nlo.ChunkString(\"1234567\", 2)\n// []string{\"12\", \"34\", \"56\", \"7\"}\n\nlo.ChunkString(\"\", 2)\n// []string{\"\"}\n\nlo.ChunkString(\"1\", 2)\n// []string{\"1\"}\n```\n\n[[play](https://go.dev/play/p/__FLTuJVz54)]\n\n### RuneLength\n\nAn alias to utf8.RuneCountInString which returns the number of runes in string.\n\n```go\nsub := lo.RuneLength(\"hellô\")\n// 5\n\nsub := len(\"hellô\")\n// 6\n```\n\n[[play](https://go.dev/play/p/tuhgW_lWY8l)]\n\n### PascalCase\n\nConverts string to pascal case.\n\n```go\nstr := lo.PascalCase(\"hello_world\")\n// HelloWorld\n```\n\n[[play](https://go.dev/play/p/iZkdeLP9oiB)]\n\n### CamelCase\n\nConverts string to camel case.\n\n```go\nstr := lo.CamelCase(\"hello_world\")\n// helloWorld\n```\n\n[[play](https://go.dev/play/p/dtyFB58MBRp)]\n\n### KebabCase\n\nConverts string to kebab case.\n\n```go\nstr := lo.KebabCase(\"helloWorld\")\n// hello-world\n```\n\n[[play](https://go.dev/play/p/2YTuPafwECA)]\n\n### SnakeCase\n\nConverts string to snake case.\n\n```go\nstr := lo.SnakeCase(\"HelloWorld\")\n// hello_world\n```\n\n[[play](https://go.dev/play/p/QVKJG9nOnDg)]\n\n### Words\n\nSplits string into an array of its words.\n\n```go\nstr := lo.Words(\"helloWorld\")\n// []string{\"hello\", \"world\"}\n```\n\n[[play](https://go.dev/play/p/2P4zhqqq61g)]\n\n### Capitalize\n\nConverts the first character of string to upper case and the remaining to lower case.\n\n```go\nstr := lo.Capitalize(\"heLLO\")\n// Hello\n```\n\n### Ellipsis\n\nTrims and truncates a string to a specified length and appends an ellipsis if truncated.\n\n```go\nstr := lo.Ellipsis(\"  Lorem Ipsum  \", 5)\n// Lo...\n\nstr := lo.Ellipsis(\"Lorem Ipsum\", 100)\n// Lorem Ipsum\n\nstr := lo.Ellipsis(\"Lorem Ipsum\", 3)\n// ...\n```\n\n### T2 -\u003e T9\n\nCreates a tuple from a list of values.\n\n```go\ntuple1 := lo.T2(\"x\", 1)\n// Tuple2[string, int]{A: \"x\", B: 1}\n\nfunc example() (string, int) { return \"y\", 2 }\ntuple2 := lo.T2(example())\n// Tuple2[string, int]{A: \"y\", B: 2}\n```\n\n[[play](https://go.dev/play/p/IllL3ZO4BQm)]\n\n### Unpack2 -\u003e Unpack9\n\nReturns values contained in tuple.\n\n```go\nr1, r2 := lo.Unpack2(lo.Tuple2[string, int]{\"a\", 1})\n// \"a\", 1\n```\n\nUnpack is also available as a method of TupleX.\n\n```go\ntuple2 := lo.T2(\"a\", 1)\na, b := tuple2.Unpack()\n// \"a\", 1\n```\n\n[[play](https://go.dev/play/p/xVP_k0kJ96W)]\n\n### Zip2 -\u003e Zip9\n\nZip creates a slice of grouped elements, the first of which contains the first elements of the given arrays, the second of which contains the second elements of the given arrays, and so on.\n\nWhen collections have different size, the Tuple attributes are filled with zero value.\n\n```go\ntuples := lo.Zip2([]string{\"a\", \"b\"}, []int{1, 2})\n// []Tuple2[string, int]{{A: \"a\", B: 1}, {A: \"b\", B: 2}}\n```\n\n[[play](https://go.dev/play/p/jujaA6GaJTp)]\n\n### ZipBy2 -\u003e ZipBy9\n\nZipBy creates a slice of transformed elements, the first of which contains the first elements of the given arrays, the second of which contains the second elements of the given arrays, and so on.\n\nWhen collections have different size, the Tuple attributes are filled with zero value.\n\n```go\nitems := lo.ZipBy2([]string{\"a\", \"b\"}, []int{1, 2}, func(a string, b int) string {\n    return fmt.Sprintf(\"%s-%d\", a, b)\n})\n// []string{\"a-1\", \"b-2\"}\n```\n\n### Unzip2 -\u003e Unzip9\n\nUnzip accepts an array of grouped elements and creates an array regrouping the elements to their pre-zip configuration.\n\n```go\na, b := lo.Unzip2([]Tuple2[string, int]{{A: \"a\", B: 1}, {A: \"b\", B: 2}})\n// []string{\"a\", \"b\"}\n// []int{1, 2}\n```\n\n[[play](https://go.dev/play/p/ciHugugvaAW)]\n\n### UnzipBy2 -\u003e UnzipBy9\n\nUnzipBy2 iterates over a collection and creates an array regrouping the elements to their pre-zip configuration.\n\n```go\na, b := lo.UnzipBy2([]string{\"hello\", \"john\", \"doe\"}, func(str string) (string, int) {\n    return str, len(str)\n})\n// []string{\"hello\", \"john\", \"doe\"}\n// []int{5, 4, 3}\n```\n\n### CrossJoin2 -\u003e CrossJoin9\n\nCombines every items from one list with every items from others. It is the cartesian product of lists received as arguments. It returns an empty list if a list is empty.\n\n```go\nresult := lo.CrossJoin2([]string{\"hello\", \"john\", \"doe\"}, []int{1, 2})\n// lo.Tuple2{\"hello\", 1}\n// lo.Tuple2{\"hello\", 2}\n// lo.Tuple2{\"john\", 1}\n// lo.Tuple2{\"john\", 2}\n// lo.Tuple2{\"doe\", 1}\n// lo.Tuple2{\"doe\", 2}\n```\n\n### CrossJoinBy2 -\u003e CrossJoinBy9\n\nCombines every items from one list with every items from others. It is the cartesian product of lists received as arguments. The project function is used to create the output values. It returns an empty list if a list is empty.\n\n```go\nresult := lo.CrossJoinBy2([]string{\"hello\", \"john\", \"doe\"}, []int{1, 2}, func(a A, b B) string {\n    return fmt.Sprintf(\"%s - %d\", a, b)\n})\n// \"hello - 1\"\n// \"hello - 2\"\n// \"john - 1\"\n// \"john - 2\"\n// \"doe - 1\"\n// \"doe - 2\"\n```\n\n### Duration\n\nReturns the time taken to execute a function.\n\n```go\nduration := lo.Duration(func() {\n    // very long job\n})\n// 3s\n```\n\n### Duration0 -\u003e Duration10\n\nReturns the time taken to execute a function.\n\n```go\nduration := lo.Duration0(func() {\n    // very long job\n})\n// 3s\n\nerr, duration := lo.Duration1(func() error {\n    // very long job\n    return fmt.Errorf(\"an error\")\n})\n// an error\n// 3s\n\nstr, nbr, err, duration := lo.Duration3(func() (string, int, error) {\n    // very long job\n    return \"hello\", 42, nil\n})\n// hello\n// 42\n// nil\n// 3s\n```\n\n### ChannelDispatcher\n\nDistributes messages from input channels into N child channels. Close events are propagated to children.\n\nUnderlying channels can have a fixed buffer capacity or be unbuffered when cap is 0.\n\n```go\nch := make(chan int, 42)\nfor i := 0; i \u003c= 10; i++ {\n    ch \u003c- i\n}\n\nchildren := lo.ChannelDispatcher(ch, 5, 10, DispatchingStrategyRoundRobin[int])\n// []\u003c-chan int{...}\n\nconsumer := func(c \u003c-chan int) {\n    for {\n        msg, ok := \u003c-c\n        if !ok {\n            println(\"closed\")\n\n            break\n        }\n\n        println(msg)\n    }\n}\n\nfor i := range children {\n    go consumer(children[i])\n}\n```\n\nMany distributions strategies are available:\n\n- [lo.DispatchingStrategyRoundRobin](./channel.go): Distributes messages in a rotating sequential manner.\n- [lo.DispatchingStrategyRandom](./channel.go): Distributes messages in a random manner.\n- [lo.DispatchingStrategyWeightedRandom](./channel.go): Distributes messages in a weighted manner.\n- [lo.DispatchingStrategyFirst](./channel.go): Distributes messages in the first non-full channel.\n- [lo.DispatchingStrategyLeast](./channel.go): Distributes messages in the emptiest channel.\n- [lo.DispatchingStrategyMost](./channel.go): Distributes to the fullest channel.\n\nSome strategies bring fallback, in order to favor non-blocking behaviors. See implementations.\n\nFor custom strategies, just implement the `lo.DispatchingStrategy` prototype:\n\n```go\ntype DispatchingStrategy[T any] func(message T, messageIndex uint64, channels []\u003c-chan T) int\n```\n\nEg:\n\n```go\ntype Message struct {\n    TenantID uuid.UUID\n}\n\nfunc hash(id uuid.UUID) int {\n    h := fnv.New32a()\n    h.Write([]byte(id.String()))\n    return int(h.Sum32())\n}\n\n// Routes messages per TenantID.\ncustomStrategy := func(message string, messageIndex uint64, channels []\u003c-chan string) int {\n    destination := hash(message) % len(channels)\n\n    // check if channel is full\n    if len(channels[destination]) \u003c cap(channels[destination]) {\n        return destination\n    }\n\n    // fallback when child channel is full\n    return utils.DispatchingStrategyRoundRobin(message, uint64(destination), channels)\n}\n\nchildren := lo.ChannelDispatcher(ch, 5, 10, customStrategy)\n...\n```\n\n### SliceToChannel\n\nReturns a read-only channels of collection elements. Channel is closed after last element. Channel capacity can be customized.\n\n```go\nlist := []int{1, 2, 3, 4, 5}\n\nfor v := range lo.SliceToChannel(2, list) {\n    println(v)\n}\n// prints 1, then 2, then 3, then 4, then 5\n```\n\n### ChannelToSlice\n\nReturns a slice built from channels items. Blocks until channel closes.\n\n```go\nlist := []int{1, 2, 3, 4, 5}\nch := lo.SliceToChannel(2, list)\n\nitems := ChannelToSlice(ch)\n// []int{1, 2, 3, 4, 5}\n```\n\n### Generator\n\nImplements the generator design pattern. Channel is closed after last element. Channel capacity can be customized.\n\n```go\ngenerator := func(yield func(int)) {\n    yield(1)\n    yield(2)\n    yield(3)\n}\n\nfor v := range lo.Generator(2, generator) {\n    println(v)\n}\n// prints 1, then 2, then 3\n```\n\n### Buffer\n\nCreates a slice of n elements from a channel. Returns the slice, the slice length, the read time and the channel status (opened/closed).\n\n```go\nch := lo.SliceToChannel(2, []int{1, 2, 3, 4, 5})\n\nitems1, length1, duration1, ok1 := lo.Buffer(ch, 3)\n// []int{1, 2, 3}, 3, 0s, true\nitems2, length2, duration2, ok2 := lo.Buffer(ch, 3)\n// []int{4, 5}, 2, 0s, false\n```\n\nExample: RabbitMQ consumer 👇\n\n```go\nch := readFromQueue()\n\nfor {\n    // read 1k items\n    items, length, _, ok := lo.Buffer(ch, 1000)\n\n    // do batching stuff\n\n    if !ok {\n        break\n    }\n}\n```\n\n### BufferWithContext\n\nCreates a slice of n elements from a channel, with timeout. Returns the slice, the slice length, the read time and the channel status (opened/closed).\n\n```go\nctx, cancel := context.WithCancel(context.TODO())\ngo func() {\n    ch \u003c- 0\n    time.Sleep(10*time.Millisecond)\n    ch \u003c- 1\n    time.Sleep(10*time.Millisecond)\n    ch \u003c- 2\n    time.Sleep(10*time.Millisecond)\n    ch \u003c- 3\n    time.Sleep(10*time.Millisecond)\n    ch \u003c- 4\n    time.Sleep(10*time.Millisecond)\n    cancel()\n}()\n\nitems1, length1, duration1, ok1 := lo.BufferWithContext(ctx, ch, 3)\n// []int{0, 1, 2}, 3, 20ms, true\nitems2, length2, duration2, ok2 := lo.BufferWithContext(ctx, ch, 3)\n// []int{3, 4}, 2, 30ms, false\n```\n\n### BufferWithTimeout\n\nCreates a slice of n elements from a channel, with timeout. Returns the slice, the slice length, the read time and the channel status (opened/closed).\n\n```go\ngenerator := func(yield func(int)) {\n    for i := 0; i \u003c 5; i++ {\n        yield(i)\n        time.Sleep(35*time.Millisecond)\n    }\n}\n\nch := lo.Generator(0, generator)\n\nitems1, length1, duration1, ok1 := lo.BufferWithTimeout(ch, 3, 100*time.Millisecond)\n// []int{1, 2}, 2, 100ms, true\nitems2, length2, duration2, ok2 := lo.BufferWithTimeout(ch, 3, 100*time.Millisecond)\n// []int{3, 4, 5}, 3, 75ms, true\nitems3, length3, duration2, ok3 := lo.BufferWithTimeout(ch, 3, 100*time.Millisecond)\n// []int{}, 0, 10ms, false\n```\n\nExample: RabbitMQ consumer 👇\n\n```go\nch := readFromQueue()\n\nfor {\n    // read 1k items\n    // wait up to 1 second\n    items, length, _, ok := lo.BufferWithTimeout(ch, 1000, 1*time.Second)\n\n    // do batching stuff\n\n    if !ok {\n        break\n    }\n}\n```\n\nExample: Multithreaded RabbitMQ consumer 👇\n\n```go\nch := readFromQueue()\n\n// 5 workers\n// prefetch 1k messages per worker\nchildren := lo.ChannelDispatcher(ch, 5, 1000, lo.DispatchingStrategyFirst[int])\n\nconsumer := func(c \u003c-chan int) {\n    for {\n        // read 1k items\n        // wait up to 1 second\n        items, length, _, ok := lo.BufferWithTimeout(ch, 1000, 1*time.Second)\n\n        // do batching stuff\n\n        if !ok {\n            break\n        }\n    }\n}\n\nfor i := range children {\n    go consumer(children[i])\n}\n```\n\n### FanIn\n\nMerge messages from multiple input channels into a single buffered channel. Output messages has no priority. When all upstream channels reach EOF, downstream channel closes.\n\n```go\nstream1 := make(chan int, 42)\nstream2 := make(chan int, 42)\nstream3 := make(chan int, 42)\n\nall := lo.FanIn(100, stream1, stream2, stream3)\n// \u003c-chan int\n```\n\n### FanOut\n\nBroadcasts all the upstream messages to multiple downstream channels. When upstream channel reach EOF, downstream channels close. If any downstream channels is full, broadcasting is paused.\n\n```go\nstream := make(chan int, 42)\n\nall := lo.FanOut(5, 100, stream)\n// [5]\u003c-chan int\n```\n\n### Contains\n\nReturns true if an element is present in a collection.\n\n```go\npresent := lo.Contains([]int{0, 1, 2, 3, 4, 5}, 5)\n// true\n```\n\n### ContainsBy\n\nReturns true if the predicate function returns `true`.\n\n```go\npresent := lo.ContainsBy([]int{0, 1, 2, 3, 4, 5}, func(x int) bool {\n    return x == 3\n})\n// true\n```\n\n### Every\n\nReturns true if all elements of a subset are contained into a collection or if the subset is empty.\n\n```go\nok := lo.Every([]int{0, 1, 2, 3, 4, 5}, []int{0, 2})\n// true\n\nok := lo.Every([]int{0, 1, 2, 3, 4, 5}, []int{0, 6})\n// false\n```\n\n### EveryBy\n\nReturns true if the predicate returns true for all elements in the collection or if the collection is empty.\n\n```go\nb := EveryBy([]int{1, 2, 3, 4}, func(x int) bool {\n    return x \u003c 5\n})\n// true\n```\n\n### Some\n\nReturns true if at least 1 element of a subset is contained into a collection.\nIf the subset is empty Some returns false.\n\n```go\nok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{0, 6})\n// true\n\nok := lo.Some([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6})\n// false\n```\n\n### SomeBy\n\nReturns true if the predicate returns true for any of the elements in the collection.\nIf the collection is empty SomeBy returns false.\n\n```go\nb := SomeBy([]int{1, 2, 3, 4}, func(x int) bool {\n    return x \u003c 3\n})\n// true\n```\n\n### None\n\nReturns true if no element of a subset are contained into a collection or if the subset is empty.\n\n```go\nb := None([]int{0, 1, 2, 3, 4, 5}, []int{0, 2})\n// false\nb := None([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6})\n// true\n```\n\n### NoneBy\n\nReturns true if the predicate returns true for none of the elements in the collection or if the collection is empty.\n\n```go\nb := NoneBy([]int{1, 2, 3, 4}, func(x int) bool {\n    return x \u003c 0\n})\n// true\n```\n\n### Intersect\n\nReturns the intersection between two collections.\n\n```go\nresult1 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{0, 2})\n// []int{0, 2}\n\nresult2 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{0, 6})\n// []int{0}\n\nresult3 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6})\n// []int{}\n```\n\n### Difference\n\nReturns the difference between two collections.\n\n- The first value is the collection of element absent of list2.\n- The second value is the collection of element absent of list1.\n\n```go\nleft, right := lo.Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 2, 6})\n// []int{1, 3, 4, 5}, []int{6}\n\nleft, right := lo.Difference([]int{0, 1, 2, 3, 4, 5}, []int{0, 1, 2, 3, 4, 5})\n// []int{}, []int{}\n```\n\n### Union\n\nReturns all distinct elements from given collections. Result will not change the order of elements relatively.\n\n```go\nunion := lo.Union([]int{0, 1, 2, 3, 4, 5}, []int{0, 2}, []int{0, 10})\n// []int{0, 1, 2, 3, 4, 5, 10}\n```\n\n### Without\n\nReturns slice excluding all given values.\n\n```go\nsubset := lo.Without([]int{0, 2, 10}, 2)\n// []int{0, 10}\n\nsubset := lo.Without([]int{0, 2, 10}, 0, 1, 2, 3, 4, 5)\n// []int{10}\n```\n\n### WithoutBy\n\nFilters a slice by excluding elements whose extracted keys match any in the exclude list.\n\nIt returns a new slice containing only the elements whose keys are not in the exclude list.\n\n\n```go\ntype struct User {\n    ID int\n    Name string\n}\n\n// original users\nusers := []User{\n    {ID: 1, Name: \"Alice\"},\n    {ID: 2, Name: \"Bob\"},\n    {ID: 3, Name: \"Charlie\"},\n}\n\n// extract function to get the user ID\ngetID := func(user User) int {\n    return user.ID\n}\n\n// exclude users with IDs 2 and 3\nexcludedIDs := []int{2, 3}\n\n// filtering users\nfilteredUsers := lo.WithoutBy(users, getID, excludedIDs...)\n// []User[{ID: 1, Name: \"Alice\"}]\n```\n\n### WithoutEmpty\n\nReturns slice excluding zero values.\n\n```go\nsubset := lo.WithoutEmpty([]int{0, 2, 10})\n// []int{2, 10}\n```\n\n### WithoutNth\n\nReturns slice excluding nth value.\n\n```go\nsubset := lo.WithoutNth([]int{-2, -1, 0, 1, 2}, 3, -42, 1)\n// []int{-2, 0, 2}\n```\n\n### ElementsMatch\n\nReturns true if lists contain the same set of elements (including empty set).\n\nIf there are duplicate elements, the number of appearances of each of them in both lists should match.\n\nThe order of elements is not checked.\n\n```go\nb := lo.ElementsMatch([]int{1, 1, 2}, []int{2, 1, 1})\n// true\n```\n\n### ElementsMatchBy\n\nReturns true if lists contain the same set of elements' keys (including empty set).\n\nIf there are duplicate keys, the number of appearances of each of them in both lists should match.\n\nThe order of elements is not checked.\n\n```go\nb := lo.ElementsMatchBy(\n    []someType{a, b},\n    []someType{b, a},\n    func(item someType) string { return item.ID() },\n)\n// true\n```\n\n### IndexOf\n\nReturns the index at which the first occurrence of a value is found in an array or return -1 if the value cannot be found.\n\n```go\nfound := lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 2)\n// 2\n\nnotFound := lo.IndexOf([]int{0, 1, 2, 1, 2, 3}, 6)\n// -1\n```\n\n### LastIndexOf\n\nReturns the index at which the last occurrence of a value is found in an array or return -1 if the value cannot be found.\n\n```go\nfound := lo.LastIndexOf([]int{0, 1, 2, 1, 2, 3}, 2)\n// 4\n\nnotFound := lo.LastIndexOf([]int{0, 1, 2, 1, 2, 3}, 6)\n// -1\n```\n\n### Find\n\nSearch an element in a slice based on a predicate. It returns element and true if element was found.\n\n```go\nstr, ok := lo.Find([]string{\"a\", \"b\", \"c\", \"d\"}, func(i string) bool {\n    return i == \"b\"\n})\n// \"b\", true\n\nstr, ok := lo.Find([]string{\"foobar\"}, func(i string) bool {\n    return i == \"b\"\n})\n// \"\", false\n```\n\n### FindIndexOf\n\nFindIndexOf searches an element in a slice based on a predicate and returns the index and true. It returns -1 and false if the element is not found.\n\n```go\nstr, index, ok := lo.FindIndexOf([]string{\"a\", \"b\", \"a\", \"b\"}, func(i string) bool {\n    return i == \"b\"\n})\n// \"b\", 1, true\n\nstr, index, ok := lo.FindIndexOf([]string{\"foobar\"}, func(i string) bool {\n    return i == \"b\"\n})\n// \"\", -1, false\n```\n\n### FindLastIndexOf\n\nFindLastIndexOf searches an element in a slice based on a predicate and returns the index and true. It returns -1 and false if the element is not found.\n\n```go\nstr, index, ok := lo.FindLastIndexOf([]string{\"a\", \"b\", \"a\", \"b\"}, func(i string) bool {\n    return i == \"b\"\n})\n// \"b\", 4, true\n\nstr, index, ok := lo.FindLastIndexOf([]string{\"foobar\"}, func(i string) bool {\n    return i == \"b\"\n})\n// \"\", -1, false\n```\n\n### FindOrElse\n\nSearch an element in a slice based on a predicate. It returns the element if found or a given fallback value otherwise.\n\n```go\nstr := lo.FindOrElse([]string{\"a\", \"b\", \"c\", \"d\"}, \"x\", func(i string) bool {\n    return i == \"b\"\n})\n// \"b\"\n\nstr := lo.FindOrElse([]string{\"foobar\"}, \"x\", func(i string) bool {\n    return i == \"b\"\n})\n// \"x\"\n```\n\n### FindKey\n\nReturns the key of the first value matching.\n\n```go\nresult1, ok1 := lo.FindKey(map[string]int{\"foo\": 1, \"bar\": 2, \"baz\": 3}, 2)\n// \"bar\", true\n\nresult2, ok2 := lo.FindKey(map[string]int{\"foo\": 1, \"bar\": 2, \"baz\": 3}, 42)\n// \"\", false\n\ntype test struct {\n    foobar string\n}\nresult3, ok3 := lo.FindKey(map[string]test{\"foo\": test{\"foo\"}, \"bar\": test{\"bar\"}, \"baz\": test{\"baz\"}}, test{\"foo\"})\n// \"foo\", true\n```\n\n### FindKeyBy\n\nReturns the key of the first element predicate returns truthy for.\n\n```go\nresult1, ok1 := lo.FindKeyBy(map[string]int{\"foo\": 1, \"bar\": 2, \"baz\": 3}, func(k string, v int) bool {\n    return k == \"foo\"\n})\n// \"foo\", true\n\nresult2, ok2 := lo.FindKeyBy(map[string]int{\"foo\": 1, \"bar\": 2, \"baz\": 3}, func(k string, v int) bool {\n    return false\n})\n// \"\", false\n```\n\n### FindUniques\n\nReturns a slice with all the unique elements of the collection. The order of result values is determined by the order they occur in the array.\n\n```go\nuniqueValues := lo.FindUniques([]int{1, 2, 2, 1, 2, 3})\n// []int{3}\n```\n\n### FindUniquesBy\n\nReturns a slice with all the unique elements of the collection. The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is invoked for each element in array to generate the criterion by which uniqueness is computed.\n\n```go\nuniqueValues := lo.FindUniquesBy([]int{3, 4, 5, 6, 7}, func(i int) int {\n    return i%3\n})\n// []int{5}\n```\n\n### FindDuplicates\n\nReturns a slice with the first occurrence of each duplicated elements of the collection. The order of result values is determined by the order they occur in the array.\n\n```go\nduplicatedValues := lo.FindDuplicates([]int{1, 2, 2, 1, 2, 3})\n// []int{1, 2}\n```\n\n### FindDuplicatesBy\n\nReturns a slice with the first occurrence of each duplicated elements of the collection. The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is invoked for each element in array to generate the criterion by which uniqueness is computed.\n\n```go\nduplicatedValues := lo.FindDuplicatesBy([]int{3, 4, 5, 6, 7}, func(i int) int {\n    return i%3\n})\n// []int{3, 4}\n```\n\n### Min\n\nSearch the minimum value of a collection.\n\nReturns zero value when the collection is empty.\n\n```go\nmin := lo.Min([]int{1, 2, 3})\n// 1\n\nmin := lo.Min([]int{})\n// 0\n\nmin := lo.Min([]time.Duration{time.Second, time.Hour})\n// 1s\n```\n\n### MinIndex\n\nSearch the minimum value of a collection and the index of the minimum value.\n\nReturns (zero value, -1) when the collection is empty.\n\n```go\nmin, index := lo.MinIndex([]int{1, 2, 3})\n// 1, 0\n\nmin, index := lo.MinIndex([]int{})\n// 0, -1\n\nmin, index := lo.MinIndex([]time.Duration{time.Second, time.Hour})\n// 1s, 0\n```\n\n### MinBy\n\nSearch the minimum value of a collection using the given comparison function.\n\nIf several values of the collection are equal to the smallest value, returns the first such value.\n\nReturns zero value when the collection is empty.\n\n```go\nmin := lo.MinBy([]string{\"s1\", \"string2\", \"s3\"}, func(item string, min string) bool {\n    return len(item) \u003c len(min)\n})\n// \"s1\"\n\nmin := lo.MinBy([]string{}, func(item string, min string) bool {\n    return len(item) \u003c len(min)\n})\n// \"\"\n```\n\n### MinIndexBy\n\nSearch the minimum value of a collection using the given comparison function and the index of the minimum value.\n\nIf several values of the collection are equal to the smallest value, returns the first such value.\n\nReturns (zero value, -1) when the collection is empty.\n\n```go\nmin, index := lo.MinIndexBy([]string{\"s1\", \"string2\", \"s3\"}, func(item string, min string) bool {\n    return len(item) \u003c len(min)\n})\n// \"s1\", 0\n\nmin, index := lo.MinIndexBy([]string{}, func(item string, min string) bool {\n    return len(item) \u003c len(min)\n})\n// \"\", -1\n```\n\n### Earliest\n\nSearch the minimum time.Time of a collection.\n\nReturns zero value when the collection is empty.\n\n```go\nearliest := lo.Earliest(time.Now(), time.Time{})\n// 0001-01-01 00:00:00 +0000 UTC\n```\n\n### EarliestBy\n\nSearch the minimum time.Time of a collection using the given iteratee function.\n\nReturns zero value when the collection is empty.\n\n```go\ntype foo struct {\n    bar time.Time\n}\n\nearliest := lo.EarliestBy([]foo{{time.Now()}, {}}, func(i foo) time.Time {\n    return i.bar\n})\n// {bar:{2023-04-01 01:02:03 +0000 UTC}}\n```\n\n### Max\n\nSearch the maximum value of a collection.\n\nReturns zero value when the collection is empty.\n\n```go\nmax := lo.Max([]int{1, 2, 3})\n// 3\n\nmax := lo.Max([]int{})\n// 0\n\nmax := lo.Max([]time.Duration{time.Second, time.Hour})\n// 1h\n```\n\n### MaxIndex\n\nSearch the maximum value of a collection and the index of the maximum value.\n\nReturns (zero value, -1) when the collection is empty.\n\n```go\nmax, index := lo.MaxIndex([]int{1, 2, 3})\n// 3, 2\n\nmax, index := lo.MaxIndex([]int{})\n// 0, -1\n\nmax, index := lo.MaxIndex([]time.Duration{time.Second, time.Hour})\n// 1h, 1\n```\n\n### MaxBy\n\nSearch the maximum value of a collection using the given comparison function.\n\nIf several values of the collection are equal to the greatest value, returns the first such value.\n\nReturns zero value when the collection is empty.\n\n```go\nmax := lo.MaxBy([]string{\"string1\", \"s2\", \"string3\"}, func(item string, max string) bool {\n    return len(item) \u003e len(max)\n})\n// \"string1\"\n\nmax := lo.MaxBy([]string{}, func(item string, max string) bool {\n    return len(item) \u003e len(max)\n})\n// \"\"\n```\n\n### MaxIndexBy\n\nSearch the maximum value of a collection using the given comparison function and the index of the maximum value.\n\nIf several values of the collection are equal to the greatest value, returns the first such value.\n\nReturns (zero value, -1) when the collection is empty.\n\n```go\nmax, index := lo.MaxIndexBy([]string{\"string1\", \"s2\", \"string3\"}, func(item string, max string) bool {\n    return len(item) \u003e len(max)\n})\n// \"string1\", 0\n\nmax, index := lo.MaxIndexBy([]string{}, func(item string, max string) bool {\n    return len(item) \u003e len(max)\n})\n// \"\", -1\n```\n\n### Latest\n\nSearch the maximum time.Time of a collection.\n\nReturns zero value when the collection is empty.\n\n```go\nlatest := lo.Latest(time.Now(), time.Time{})\n// 2023-04-01 01:02:03 +0000 UTC\n```\n\n### LatestBy\n\nSearch the maximum time.Time of a collection using the given iteratee function.\n\nReturns zero value when the collection is empty.\n\n```go\ntype foo struct {\n    bar time.Time\n}\n\nlatest := lo.LatestBy([]foo{{time.Now()}, {}}, func(i foo) time.Time {\n    return i.bar\n})\n// {bar:{2023-04-01 01:02:03 +0000 UTC}}\n```\n\n### First\n\nReturns the first element of a collection and check for availability of the first element.\n\n```go\nfirst, ok := lo.First([]int{1, 2, 3})\n// 1, true\n\nfirst, ok := lo.First([]int{})\n// 0, false\n```\n\n### FirstOrEmpty\n\nReturns the first element of a collection or zero value if empty.\n\n```go\nfirst := lo.FirstOrEmpty([]int{1, 2, 3})\n// 1\n\nfirst := lo.FirstOrEmpty([]int{})\n// 0\n```\n\n### FirstOr\n\nReturns the first element of a collection or the fallback value if empty.\n\n```go\nfirst := lo.FirstOr([]int{1, 2, 3}, 245)\n// 1\n\nfirst := lo.FirstOr([]int{}, 31)\n// 31\n```\n\n### Last\n\nReturns the last element of a collection or error if empty.\n\n```go\nlast, ok := lo.Last([]int{1, 2, 3})\n// 3\n// true\n\nlast, ok := lo.Last([]int{})\n// 0\n// false\n```\n\n### LastOrEmpty\n\nReturns the first element of a collection or zero value if empty.\n\n```go\nlast := lo.LastOrEmpty([]int{1, 2, 3})\n// 3\n\nlast := lo.LastOrEmpty([]int{})\n// 0\n```\n\n### LastOr\n\nReturns the first element of a collection or the fallback value if empty.\n\n```go\nlast := lo.LastOr([]int{1, 2, 3}, 245)\n// 3\n\nlast := lo.LastOr([]int{}, 31)\n// 31\n```\n\n### Nth\n\nReturns the element at index `nth` of collection. If `nth` is negative, the nth element from the end is returned. An error is returned when nth is out of slice bounds.\n\n```go\nnth, err := lo.Nth([]int{0, 1, 2, 3}, 2)\n// 2\n\nnth, err := lo.Nth([]int{0, 1, 2, 3}, -2)\n// 2\n```\n\n### NthOr\n\nReturns the element at index `nth` of the collection. If `nth` is negative, it returns the `nth` element from the end. If `nth` is out of slice bounds, it returns the provided fallback value\n```go\t\nnth := lo.NthOr([]int{10, 20, 30, 40, 50}, 2, -1)\n// 30\n\nnth := lo.NthOr([]int{10, 20, 30, 40, 50}, -1, -1)\n// 50\n\nnth := lo.NthOr([]int{10, 20, 30, 40, 50}, 5, -1)\n// -1 (fallback value)\n```\n\n### NthOrEmpty\n\nReturns the element at index `nth` of the collection. If `nth` is negative, it returns the `nth` element from the end. If `nth` is out of slice bounds, it returns the zero value for the element type (e.g., 0 for integers, \"\" for strings, etc).\n``` go\nnth := lo.NthOrEmpty([]int{10, 20, 30, 40, 50}, 2)\n// 30\n\nnth := lo.NthOrEmpty([]int{10, 20, 30, 40, 50}, -1)\n// 50\n\nnth := lo.NthOrEmpty([]int{10, 20, 30, 40, 50}, 5)\n// 0 (zero value for int)\n\nnth := lo.NthOrEmpty([]string{\"apple\", \"banana\", \"cherry\"}, 2)\n// \"cherry\"\n\nnth := lo.NthOrEmpty([]string{\"apple\", \"banana\", \"cherry\"}, 5)\n// \"\" (zero value for string)\n```\n\n### Sample\n\nReturns a random item from collection.\n\n```go\nlo.Sample([]string{\"a\", \"b\", \"c\"})\n// a random string from []string{\"a\", \"b\", \"c\"}\n\nlo.Sample([]string{})\n// \"\"\n```\n\n\n\n### SampleBy\n\nReturns a random item from collection, using a given random integer generator.\n\n```go\nimport \"math/rand\"\n\nr := rand.New(rand.NewSource(42))\nlo.SampleBy([]string{\"a\", \"b\", \"c\"}, r.Intn)\n// a random string from []string{\"a\", \"b\", \"c\"}, using a seeded random generator\n\nlo.SampleBy([]string{}, r.Intn)\n// \"\"\n```\n\n### Samples\n\nReturns N random unique items from collection.\n\n```go\nlo.Samples([]string{\"a\", \"b\", \"c\"}, 3)\n// []string{\"a\", \"b\", \"c\"} in random order\n```\n\n### SamplesBy\n\nReturns N random unique items from collection, using a given random integer generator.\n\n```go\nr := rand.New(rand.NewSource(42))\nlo.SamplesBy([]string{\"a\", \"b\", \"c\"}, 3, r.Intn)\n// []string{\"a\", \"b\", \"c\"} in random order, using a seeded random generator\n```\n\n### Ternary\n\nA 1 line if/else statement.\n\n\n```go\nresult := lo.Ternary(true, \"a\", \"b\")\n// \"a\"\n\nresult := lo.Ternary(false, \"a\", \"b\")\n// \"b\"\n```\n\nTake care to avoid dereferencing potentially nil pointers in your A/B expressions, because they are both evaluated. See TernaryF to avoid this problem.\n\n[[play](https://go.dev/play/p/t-D7WBL44h2)]\n\n### TernaryF\n\nA 1 line if/else statement whose options are functions.\n\n```go\nresult := lo.TernaryF(true, func() string { return \"a\" }, func() string { return \"b\" })\n// \"a\"\n\nresult := lo.TernaryF(false, func() string { return \"a\" }, func() string { return \"b\" })\n// \"b\"\n```\n\nUseful to avoid nil-pointer dereferencing in initializations, or avoid running unnecessary code\n\n```go\nvar s *string\n\nsomeStr := TernaryF(s == nil, func() string { return uuid.New().String() }, func() string { return *s })\n// ef782193-c30c-4e2e-a7ae-f8ab5e125e02\n```\n\n[[play](https://go.dev/play/p/AO4VW20JoqM)]\n\n### If / ElseIf / Else\n\n```go\nresult := lo.If(true, 1).\n    ElseIf(false, 2).\n    Else(3)\n// 1\n\nresult := lo.If(false, 1).\n    ElseIf(true, 2).\n    Else(3)\n// 2\n\nresult := lo.If(false, 1).\n    ElseIf(false, 2).\n    Else(3)\n// 3\n```\n\nUsing callbacks:\n\n```go\nresult := lo.IfF(true, func () int {\n        return 1\n    }).\n    ElseIfF(false, func () int {\n        return 2\n    }).\n    ElseF(func () int {\n        return 3\n    })\n// 1\n```\n\nMixed:\n\n```go\nresult := lo.IfF(true, func () int {\n        return 1\n    }).\n    Else(42)\n// 1\n```\n\n[[play](https://go.dev/play/p/WSw3ApMxhyW)]\n\n### Switch / Case / Default\n\n```go\nresult := lo.Switch(1).\n    Case(1, \"1\").\n    Case(2, \"2\").\n    Default(\"3\")\n// \"1\"\n\nresult := lo.Switch(2).\n    Case(1, \"1\").\n    Case(2, \"2\").\n    Default(\"3\")\n// \"2\"\n\nresult := lo.Switch(42).\n    Case(1, \"1\").\n    Case(2, \"2\").\n    Default(\"3\")\n// \"3\"\n```\n\nUsing callbacks:\n\n```go\nresult := lo.Switch(1).\n    CaseF(1, func() string {\n        return \"1\"\n    }).\n    CaseF(2, func() string {\n        return \"2\"\n    }).\n    DefaultF(func() string {\n        return \"3\"\n    })\n// \"1\"\n```\n\nMixed:\n\n```go\nresult := lo.Switch(1).\n    CaseF(1, func() string {\n        return \"1\"\n    }).\n    Default(\"42\")\n// \"1\"\n```\n\n[[play](https://go.dev/play/p/TGbKUMAeRUd)]\n\n### IsNil\n\nChecks if a value is nil or if it's a reference type with a nil underlying value.\n\n```go\nvar x int\nlo.IsNil(x)\n// false\n\nvar k struct{}\nlo.IsNil(k)\n// false\n\nvar i *int\nlo.IsNil(i)\n// true\n\nvar ifaceWithNilValue any = (*string)(nil)\nlo.IsNil(ifaceWithNilValue)\n// true\nifaceWithNilValue == nil\n// false\n```\n\n### IsNotNil\n\nChecks if a value is not nil or if it's not a reference type with a nil underlying value.\n\n```go\nvar x int\nlo.IsNotNil(x)\n// true\n\nvar k struct{}\nlo.IsNotNil(k)\n// true\n\nvar i *int\nlo.IsNotNil(i)\n// false\n\nvar ifaceWithNilValue any = (*string)(nil)\nlo.IsNotNil(ifaceWithNilValue)\n// false\nifaceWithNilValue == nil\n// true\n```\n\n### ToPtr\n\nReturns a pointer copy of the value.\n\n```go\nptr := lo.ToPtr(\"hello world\")\n// *string{\"hello world\"}\n```\n\n### Nil\n\nReturns a nil pointer of type.\n\n```go\nptr := lo.Nil[float64]()\n// nil\n```\n\n### EmptyableToPtr\n\nReturns a pointer copy of value if it's nonzero.\nOtherwise, returns nil pointer.\n\n```go\nptr := lo.EmptyableToPtr(nil)\n// nil\n\nptr := lo.EmptyableToPtr(\"\")\n// nil\n\nptr := lo.EmptyableToPtr([]int{})\n// *[]int{}\n\nptr := lo.EmptyableToPtr(\"hello world\")\n// *string{\"hello world\"}\n```\n\n### FromPtr\n\nReturns the pointer value or empty.\n\n```go\nstr := \"hello world\"\nvalue := lo.FromPtr(\u0026str)\n// \"hello world\"\n\nvalue := lo.FromPtr(nil)\n// \"\"\n```\n\n### FromPtrOr\n\nReturns the pointer value or the fallback value.\n\n```go\nstr := \"hello world\"\nvalue := lo.FromPtrOr(\u0026str, \"empty\")\n// \"hello world\"\n\nvalue := lo.FromPtrOr(nil, \"empty\")\n// \"empty\"\n```\n\n### ToSlicePtr\n\nReturns a slice of pointer copy of value.\n\n```go\nptr := lo.ToSlicePtr([]string{\"hello\", \"world\"})\n// []*string{\"hello\", \"world\"}\n```\n\n### FromSlicePtr\n\nReturns a slice with the pointer values.\nReturns a zero value in case of a nil pointer element.\n\n```go\nstr1 := \"hello\"\nstr2 := \"world\"\n\nptr := lo.FromSlicePtr[string]([]*string{\u0026str1, \u0026str2, nil})\n// []string{\"hello\", \"world\", \"\"}\n\nptr := lo.Compact(\n    lo.FromSlicePtr[string]([]*string{\u0026str1, \u0026str2, nil}),\n)\n// []string{\"hello\", \"world\"}\n```\n\n### FromSlicePtrOr\n\nReturns a slice with the pointer values or the fallback value.\n\n```go\nstr1 := \"hello\"\nstr2 := \"world\"\n\nptr := lo.FromSlicePtrOr([]*string{\u0026str1, nil, \u0026str2}, \"fallback value\")\n// []string{\"hello\", \"fallback value\", \"world\"}\n```\n\n[[play](https://go.dev/play/p/CuXGVzo9G65)]\n\n### ToAnySlice\n\nReturns a slice with all elements mapped to `any` type.\n\n```go\nelements := lo.ToAnySlice([]int{1, 5, 1})\n// []any{1, 5, 1}\n```\n\n### FromAnySlice\n\nReturns an `any` slice with all elements mapped to a type. Returns false in case of type conversion failure.\n\n```go\nelements, ok := lo.FromAnySlice([]any{\"foobar\", 42})\n// []string{}, false\n\nelements, ok := lo.FromAnySlice([]any{\"foobar\", \"42\"})\n// []string{\"foobar\", \"42\"}, true\n```\n\n### Empty\n\nReturns the [zero value](https://go.dev/ref/spec#The_zero_value).\n\n```go\nlo.Empty[int]()\n// 0\nlo.Empty[string]()\n// \"\"\nlo.Empty[bool]()\n// false\n```\n\n### IsEmpty\n\nReturns true if argument is a zero value.\n\n```go\nlo.IsEmpty(0)\n// true\nlo.IsEmpty(42)\n// false\n\nlo.IsEmpty(\"\")\n// true\nlo.IsEmpty(\"foobar\")\n// false\n\ntype test struct {\n    foobar string\n}\n\nlo.IsEmpty(test{foobar: \"\"})\n// true\nlo.IsEmpty(test{foobar: \"foobar\"})\n// false\n```\n\n### IsNotEmpty\n\nReturns true if argument is a zero value.\n\n```go\nlo.IsNotEmpty(0)\n// false\nlo.IsNotEmpty(42)\n// true\n\nlo.IsNotEmpty(\"\")\n// false\nlo.IsNotEmpty(\"foobar\")\n// true\n\ntype test struct {\n    foobar string\n}\n\nlo.IsNotEmpty(test{foobar: \"\"})\n// false\nlo.IsNotEmpty(test{foobar: \"foobar\"})\n// true\n```\n\n### Coalesce\n\nReturns the first non-empty arguments. Arguments must be comparable.\n\n```go\nresult, ok := lo.Coalesce(0, 1, 2, 3)\n// 1 true\n\nresult, ok := lo.Coalesce(\"\")\n// \"\" false\n\nvar nilStr *string\nstr := \"foobar\"\nresult, ok := lo.Coalesce(nil, nilStr, \u0026str)\n// \u0026\"foobar\" true\n```\n\n### CoalesceOrEmpty\n\nReturns the first non-empty arguments. Arguments must be comparable.\n\n```go\nresult := lo.CoalesceOrEmpty(0, 1, 2, 3)\n// 1\n\nresult := lo.CoalesceOrEmpty(\"\")\n// \"\"\n\nvar nilStr *string\nstr := \"foobar\"\nresult := lo.CoalesceOrEmpty(nil, nilStr, \u0026str)\n// \u0026\"foobar\"\n```\n\n### CoalesceSlice\n\nReturns the first non-zero slice.\n\n```go\nresult, ok := lo.CoalesceSlice([]int{1, 2, 3}, []int{4, 5, 6})\n// [1, 2, 3]\n// true\n\nresult, ok := lo.CoalesceSlice(nil, []int{})\n// []\n// true\n\nresult, ok := lo.CoalesceSlice([]int(nil))\n// []\n// false\n```\n\n### CoalesceSliceOrEmpty\n\nReturns the first non-zero slice.\n\n```go\nresult := lo.CoalesceSliceOrEmpty([]int{1, 2, 3}, []int{4, 5, 6})\n// [1, 2, 3]\n\nresult := lo.CoalesceSliceOrEmpty(nil, []int{})\n// []\n```\n\n### CoalesceMap\n\nReturns the first non-zero map.\n\n```go\nresult, ok := lo.CoalesceMap(map[string]int{\"1\": 1, \"2\": 2, \"3\": 3}, map[string]int{\"4\": 4, \"5\": 5, \"6\": 6})\n// {\"1\": 1, \"2\": 2, \"3\": 3}\n// true\n\nresult, ok := lo.CoalesceMap(nil, map[string]int{})\n// {}\n// true\n\nresult, ok := lo.CoalesceMap(map[string]int(nil))\n// {}\n// false\n```\n\n### CoalesceMapOrEmpty\n\nReturns the first non-zero map.\n\n```go\nresult := lo.CoalesceMapOrEmpty(map[string]int{\"1\": 1, \"2\": 2, \"3\": 3}, map[string]int{\"4\": 4, \"5\": 5, \"6\": 6})\n// {\"1\": 1, \"2\": 2, \"3\": 3}\n\nresult := lo.CoalesceMapOrEmpty(nil, map[string]int{})\n// {}\n```\n\n### Partial\n\nReturns new function that, when called, has its first argument set to the provided value.\n\n```go\nadd := func(x, y int) int { return x + y }\nf := lo.Partial(add, 5)\n\nf(10)\n// 15\n\nf(42)\n// 47\n```\n\n### Partial2 -\u003e Partial5\n\nReturns new function that, when called, has its first argument set to the provided value.\n\n```go\nadd := func(x, y, z int) int { return x + y + z }\nf := lo.Partial2(add, 42)\n\nf(10, 5)\n// 57\n\nf(42, -4)\n// 80\n```\n\n### Attempt\n\nInvokes a function N times until it returns valid output. Returns either the caught error or nil.\n\nWhen the first argument is less than `1`, the function runs until a successful response is returned.\n\n```go\niter, err := lo.Attempt(42, func(i int) error {\n    if i == 5 {\n        return nil\n    }\n\n    return fmt.Errorf(\"failed\")\n})\n// 6\n// nil\n\niter, err := lo.Attempt(2, func(i int) error {\n    if i == 5 {\n        return nil\n    }\n\n    return fmt.Errorf(\"failed\")\n})\n// 2\n// error \"failed\"\n\niter, err := lo.Attempt(0, func(i int) error {\n    if i \u003c 42 {\n        return fmt.Errorf(\"failed\")\n    }\n\n    return nil\n})\n// 43\n// nil\n```\n\nFor more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff).\n\n[[play](https://go.dev/play/p/3ggJZ2ZKcMj)]\n\n### AttemptWithDelay\n\nInvokes a function N times until it returns valid output, with a pause between each call. Returns either the caught error or nil.\n\nWhen the first argument is less than `1`, the function runs until a successful response is returned.\n\n```go\niter, duration, err := lo.AttemptWithDelay(5, 2*time.Second, func(i int, duration time.Duration) error {\n    if i == 2 {\n        return nil\n    }\n\n    return fmt.Errorf(\"failed\")\n})\n// 3\n// ~ 4 seconds\n// nil\n```\n\nFor more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff).\n\n[[play](https://go.dev/play/p/tVs6CygC7m1)]\n\n### AttemptWhile\n\nInvokes a function N times until it returns valid output. Returns either the caught error or nil, along with a bool value to determine whether the function should be invoked again. It will terminate the invoke immediately if the second return value is false.\n\nWhen the first argument is less than `1`, the function runs until a successful response is returned.\n\n```go\ncount1, err1 := lo.AttemptWhile(5, func(i int) (error, bool) {\n    err := doMockedHTTPRequest(i)\n    if err != nil {\n        if errors.Is(err, ErrBadRequest) { // lets assume ErrBadRequest is a critical error that needs to terminate the invoke\n            return err, false // flag the second return value as false to terminate the invoke\n        }\n\n        return err, true\n    }\n\n    return nil, false\n})\n```\n\nFor more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff).\n\n[[play](https://go.dev/play/p/M2wVq24PaZM)]\n\n### AttemptWhileWithDelay\n\nInvokes a function N times until it returns valid output, with a pause between each call. Returns either the caught error or nil, along with a bool value to determine whether the function should be invoked again. It will terminate the invoke immediately if the second return value is false.\n\nWhen the first argument is less than `1`, the function runs until a successful response is returned.\n\n```go\ncount1, time1, err1 := lo.AttemptWhileWithDelay(5, time.Millisecond, func(i int, d time.Duration) (error, bool) {\n    err := doMockedHTTPRequest(i)\n    if err != nil {\n        if errors.Is(err, ErrBadRequest) { // lets assume ErrBadRequest is a critical error that needs to terminate the invoke\n            return err, false // flag the second return value as false to terminate the invoke\n        }\n\n        return err, true\n    }\n\n    return nil, false\n})\n```\n\nFor more advanced retry strategies (delay, exponential backoff...), please take a look on [cenkalti/backoff](https://github.com/cenkalti/backoff).\n\n[[play](https://go.dev/play/p/cfcmhvLO-nv)]\n\n### Debounce\n\n`NewDebounce` creates a debounced instance that delays invoking functions given until after wait milliseconds have elapsed, until `cancel` is called.\n\n```go\nf := func() {\n    println(\"Called once after 100ms when debounce stopped invoking!\")\n}\n\ndebounce, cancel := lo.NewDebounce(100 * time.Millisecond, f)\nfor j := 0; j \u003c 10; j++ {\n    debounce()\n}\n\ntime.Sleep(1 * time.Second)\ncancel()\n```\n\n[[play](https://go.dev/play/p/mz32VMK2nqe)]\n\n### DebounceBy\n\n`NewDebounceBy` creates a debounced instance for each distinct key, that delays invoking functions given until after wait milliseconds have elapsed, until `cancel` is called.\n\n```go\nf := func(key string, count int) {\n    println(key + \": Called once after 100ms when debounce stopped invoking!\")\n}\n\ndebounce, cancel := lo.NewDebounceBy(100 * time.Millisecond, f)\nfor j := 0; j \u003c 10; j++ {\n    debounce(\"first key\")\n    debounce(\"second key\")\n}\n\ntime.Sleep(1 * time.Second)\ncancel(\"first key\")\ncancel(\"second key\")\n```\n\n[[play](https://go.dev/play/p/d3Vpt6pxhY8)]\n\n### Throttle\n\nCreates a throttled instance that invokes given functions only once in every interval.\n\nThis returns 2 functions, First one is throttled function and Second one is a function to reset interval.\n\n```go\nf := func() {\n\tprintln(\"Called once in every 100ms\")\n}\n\nthrottle, reset := lo.NewThrottle(100 * time.Millisecond, f)\n\nfor j := 0; j \u003c 10; j++ {\n\tthrottle()\n\ttime.Sleep(30 * time.Millisecond)\n}\n\nreset()\nthrottle()\n```\n\n`NewThrottleWithCount` is NewThrottle with count limit, throttled function will be invoked count times in every interval.\n\n```go\nf := func() {\n\tprintln(\"Called three times in every 100ms\")\n}\n\nthrottle, reset := lo.NewThrottleWithCount(100 * time.Millisecond, f)\n\nfor j := 0; j \u003c 10; j++ {\n\tthrottle()\n\ttime.Sleep(30 * time.Millisecond)\n}\n\nreset()\nthrottle()\n```\n\n`NewThrottleBy` and `NewThrottleByWithCount` are NewThrottle with sharding key, throttled function will be invoked count times in every interval.\n\n```go\nf := func(key string) {\n\tprintln(key, \"Called three times in every 100ms\")\n}\n\nthrottle, reset := lo.NewThrottleByWithCount(100 * time.Millisecond, f)\n\nfor j := 0; j \u003c 10; j++ {\n\tthrottle(\"foo\")\n\ttime.Sleep(30 * time.Millisecond)\n}\n\nreset()\nthrottle()\n```\n\n### Synchronize\n\nWraps the underlying callback in a mutex. It receives an optional mutex.\n\n```go\ns := lo.Synchronize()\n\nfor i := 0; i \u003c 10; i++ {\n    go s.Do(func () {\n        println(\"will be called sequentially\")\n    })\n}\n```\n\nIt is equivalent to:\n\n```go\nmu := sync.Mutex{}\n\nfunc foobar() {\n    mu.Lock()\n    defer mu.Unlock()\n\n    // ...\n}\n```\n\n### Async\n\nExecutes a function in a goroutine and returns the result in a channel.\n\n```go\nch := lo.Async(func() error { time.Sleep(10 * time.Second); return nil })\n// chan error (nil)\n```\n\n### Async{0-\u003e6}\n\nExecutes a function in a goroutine and returns the result in a channel.\nFor function with multiple return values, the results will be returned as a tuple inside the channel.\nFor function without return, struct{} will be returned in the channel.\n\n```go\nch := lo.Async0(func() { time.Sleep(10 * time.Second) })\n// chan struct{}\n\nch := lo.Async1(func() int {\n  time.Sleep(10 * time.Second);\n  return 42\n})\n// chan int (42)\n\nch := lo.Async2(func() (int, string) {\n  time.Sleep(10 * time.Second);\n  return 42, \"Hello\"\n})\n// chan lo.Tuple2[int, string] ({42, \"Hello\"})\n```\n\n### Transaction\n\nImplements a Saga pattern.\n\n```go\ntransaction := NewTransaction().\n    Then(\n        func(state int) (int, error) {\n            fmt.Println(\"step 1\")\n            return state + 10, nil\n        },\n        func(state int) int {\n            fmt.Println(\"rollback 1\")\n            return state - 10\n        },\n    ).\n    Then(\n        func(state int) (int, error) {\n            fmt.Println(\"step 2\")\n            return state + 15, nil\n        },\n        func(state int) int {\n            fmt.Println(\"rollback 2\")\n            return state - 15\n        },\n    ).\n    Then(\n        func(state int) (int, error) {\n            fmt.Println(\"step 3\")\n\n            if true {\n                return state, fmt.Errorf(\"error\")\n            }\n\n            return state + 42, nil\n        },\n        func(state int) int {\n            fmt.Println(\"rollback 3\")\n            return state - 42\n        },\n    )\n\n_, _ = transaction.Process(-5)\n\n// Output:\n// step 1\n// step 2\n// step 3\n// rollback 2\n// rollback 1\n```\n\n### WaitFor\n\nRuns periodically until a condition is validated.\n\n```go\nalwaysTrue := func(i int) bool { return true }\nalwaysFalse := func(i int) bool { return false }\nlaterTrue := func(i int) bool {\n    return i \u003e 5\n}\n\niterations, duration, ok := lo.WaitFor(alwaysTrue, 10*time.Millisecond, 2 * time.Millisecond)\n// 1\n// 1ms\n// true\n\niterations, duration, ok := lo.WaitFor(alwaysFalse, 10*time.Millisecond, time.Millisecond)\n// 10\n// 10ms\n// false\n\niterations, duration, ok := lo.WaitFor(laterTrue, 10*time.Millisecond, time.Millisecond)\n// 7\n// 7ms\n// true\n\niterations, duration, ok := lo.WaitFor(laterTrue, 10*time.Millisecond, 5*time.Millisecond)\n// 2\n// 10ms\n// false\n```\n\n### WaitForWithContext\n\nRuns periodically until a condition is validated or context is invalid.\n\nThe condition receives also the context, so it can invalidate the process in the condition checker\n\n```go\nctx := context.Background()\n\nalwaysTrue := func(_ context.Context, i int) bool { return true }\nalwaysFalse := func(_ context.Context, i int) bool { return false }\nlaterTrue := func(_ context.Context, i int) bool {\n    return i \u003e= 5\n}\n\niterations, duration, ok := lo.WaitForWithContext(ctx, alwaysTrue, 10*time.Millisecond, 2 * time.Millisecond)\n// 1\n// 1ms\n// true\n\niterations, duration, ok := lo.WaitForWithContext(ctx, alwaysFalse, 10*time.Millisecond, time.Millisecond)\n// 10\n// 10ms\n// false\n\niterations, duration, ok := lo.WaitForWithContext(ctx, laterTrue, 10*time.Millisecond, time.Millisecond)\n// 5\n// 5ms\n// true\n\niterations, duration, ok := lo.WaitForWithContext(ctx, laterTrue, 10*time.Millisecond, 5*time.Millisecond)\n// 2\n// 10ms\n// false\n\nexpiringCtx, cancel := context.WithTimeout(ctx, 5*time.Millisecond)\niterations, duration, ok := lo.WaitForWithContext(expiringCtx, alwaysFalse, 100*time.Millisecond, time.Millisecond)\n// 5\n// 5.1ms\n// false\n```\n\n### Validate\n\nHelper function that creates an error when a condition is not met.\n\n```go\nslice := []string{\"a\"}\nval := lo.Validate(len(slice) == 0, \"Slice should be empty but contains %v\", slice)\n// error(\"Slice should be empty but contains [a]\")\n\nslice := []string{}\nval := lo.Validate(len(slice) == 0, \"Slice should be empty but contains %v\", slice)\n// nil\n```\n\n[[play](https://go.dev/play/p/vPyh51XpCBt)]\n\n### Must\n\nWraps a function call to panics if second argument is `error` or `false`, returns the value otherwise.\n\n```go\nval := lo.Must(time.Parse(\"2006-01-02\", \"2022-01-15\"))\n// 2022-01-15\n\nval := lo.Must(time.Parse(\"2006-01-02\", \"bad-value\"))\n// panics\n```\n\n[[play](https://go.dev/play/p/TMoWrRp3DyC)]\n\n### Must{0-\u003e6}\n\nMust\\* has the same behavior as Must, but returns multiple values.\n\n```go\nfunc example0() (error)\nfunc example1() (int, error)\nfunc example2() (int, string, error)\nfunc example3() (int, string, time.Date, error)\nfunc example4() (int, string, time.Date, bool, error)\nfunc example5() (int, string, time.Date, bool, float64, error)\nfunc example6() (int, string, time.Date, bool, float64, byte, error)\n\nlo.Must0(example0())\nval1 := lo.Must1(example1())    // alias to Must\nval1, val2 := lo.Must2(example2())\nval1, val2, val3 := lo.Must3(example3())\nval1, val2, val3, val4 := lo.Must4(example4())\nval1, val2, val3, val4, val5 := lo.Must5(example5())\nval1, val2, val3, val4, val5, val6 := lo.Must6(example6())\n```\n\nYou can wrap functions like `func (...) (..., ok bool)`.\n\n```go\n// math.Signbit(float64) bool\nlo.Must0(math.Signbit(v))\n\n// bytes.Cut([]byte,[]byte) ([]byte, []byte, bool)\nbefore, after := lo.Must2(bytes.Cut(s, sep))\n```\n\nYou can give context to the panic message by adding some printf-like arguments.\n\n```go\nval, ok := lo.Find(myString, func(i string) bool {\n    return i == requiredChar\n})\nlo.Must0(ok, \"'%s' must always contain '%s'\", myString, requiredChar)\n\nlist := []int{0, 1, 2}\nitem := 5\nlo.Must0(lo.Contains(list, item), \"'%s' must always contain '%s'\", list, item)\n...\n```\n\n[[play](https://go.dev/play/p/TMoWrRp3DyC)]\n\n### Try\n\nCalls the function and returns false in case of error and panic.\n\n```go\nok := lo.Try(func() error {\n    panic(\"error\")\n    return nil\n})\n// false\n\nok := lo.Try(func() error {\n    return nil\n})\n// true\n\nok := lo.Try(func() error {\n    return fmt.Errorf(\"error\")\n})\n// false\n```\n\n[[play](https://go.dev/play/p/mTyyWUvn9u4)]\n\n### Try{0-\u003e6}\n\nThe same behavior as `Try`, but the callback returns 2 variables.\n\n```go\nok := lo.Try2(func() (string, error) {\n    panic(\"error\")\n    return \"\", nil\n})\n// false\n```\n\n[[play](https://go.dev/play/p/mTyyWUvn9u4)]\n\n### TryOr\n\nCalls the function and return a default value in case of error and on panic.\n\n```go\nstr, ok := lo.TryOr(func() (string, error) {\n    panic(\"error\")\n    return \"hello\", nil\n}, \"world\")\n// world\n// false\n\nstr, ok := lo.TryOr(func() error {\n    return \"hello\", nil\n}, \"world\")\n// hello\n// true\n\nstr, ok := lo.TryOr(func() error {\n    return \"hello\", fmt.Errorf(\"error\")\n}, \"world\")\n// world\n// false\n```\n\n[[play](https://go.dev/play/p/B4F7Wg2Zh9X)]\n\n### TryOr{0-\u003e6}\n\nThe same behavior as `TryOr`, but the callback returns `X` variables.\n\n```go\nstr, nbr, ok := lo.TryOr2(func() (string, int, error) {\n    panic(\"error\")\n    return \"hello\", 42, nil\n}, \"world\", 21)\n// world\n// 21\n// false\n```\n\n[[play](https://go.dev/play/p/B4F7Wg2Zh9X)]\n\n### TryWithErrorValue\n\nThe same behavior as `Try`, but also returns the value passed to panic.\n\n```go\nerr, ok := lo.TryWithErrorValue(func() error {\n    panic(\"error\")\n    return nil\n})\n// \"error\", false\n```\n\n[[play](https://go.dev/play/p/Kc7afQIT2Fs)]\n\n### TryCatch\n\nThe same behavior as `Try`, but calls the catch function in case of error.\n\n```go\ncaught := false\n\nok := lo.TryCatch(func() error {\n    panic(\"error\")\n    return nil\n}, func() {\n    caught = true\n})\n// false\n// caught == true\n```\n\n[[play](https://go.dev/play/p/PnOON-EqBiU)]\n\n### TryCatchWithErrorValue\n\nThe same behavior as `TryWithErrorValue`, but calls the catch function in case of error.\n\n```go\ncaught := false\n\nok := lo.TryCatchWithErrorValue(func() error {\n    panic(\"error\")\n    return nil\n}, func(val any) {\n    caught = val == \"error\"\n})\n// false\n// caught == true\n```\n\n[[play](https://go.dev/play/p/8Pc9gwX_GZO)]\n\n### ErrorsAs\n\nA shortcut for:\n\n```go\nerr := doSomething()\n\nvar rateLimitErr *RateLimitError\nif ok := errors.As(err, \u0026rateLimitErr); ok {\n    // retry later\n}\n```\n\n1 line `lo` helper:\n\n```go\nerr := doSomething()\n\nif rateLimitErr, ok := lo.ErrorsAs[*RateLimitError](err); ok {\n    // retry later\n}\n```\n\n[[play](https://go.dev/play/p/8wk5rH8UfrE)]\n\n## 🛩 Benchmark\n\nWe executed a simple benchmark with a dead-simple `lo.Map` loop:\n\nSee the full implementation [here](./map_benchmark_test.go).\n\n```go\n_ = lo.Map[int64](arr, func(x int64, i int) string {\n    return strconv.FormatInt(x, 10)\n})\n```\n\n**Result:**\n\nHere is a comparison between `lo.Map`, `lop.Map`, `go-funk` library and a simple Go `for` loop.\n\n```shell\n$ go test -benchmem -bench ./...\ngoos: linux\ngoarch: amd64\npkg: github.com/samber/lo\ncpu: Intel(R) Core(TM) i5-7267U CPU @ 3.10GHz\ncpu: Intel(R) Core(TM) i7 CPU         920  @ 2.67GHz\nBenchmarkMap/lo.Map-8         \t       8\t 132728237 ns/op\t39998945 B/op\t 1000002 allocs/op\nBenchmarkMap/lop.Map-8        \t       2\t 503947830 ns/op\t119999956 B/op\t 3000007 allocs/op\nBenchmarkMap/reflect-8        \t       2\t 826400560 ns/op\t170326512 B/op\t 4000042 allocs/op\nBenchmarkMap/for-8            \t       9\t 126252954 ns/op\t39998674 B/op\t 1000001 allocs/op\nPASS\nok  \tgithub.com/samber/lo\t6.657s\n```\n\n- `lo.Map` is way faster (x7) than `go-funk`, a reflection-based Map implementation.\n- `lo.Map` has the same allocation profile as `for`.\n- `lo.Map` is 4% slower than `for`.\n- `lop.Map` is slower than `lo.Map` because it implies more memory allocation and locks. `lop.Map` will be useful for long-running callbacks, such as i/o bound processing.\n- `for` beats other implementations for memory and CPU.\n\n## 🤝 Contributing\n\n- Ping me on Twitter [@samuelberthe](https://twitter.com/samuelberthe) (DMs, mentions, whatever :))\n- Fork the [project](https://github.com/samber/lo)\n- Fix [open issues](https://github.com/samber/lo/issues) or request new features\n\nDon't hesitate ;)\n\nHelper naming: helpers must be self-explanatory and respect standards (other languages, libraries...). Feel free to suggest many names in your contributions.\n\n### With Docker\n\n```bash\ndocker-compose run --rm dev\n```\n\n### Without Docker\n\n```bash\n# Install some dev dependencies\nmake tools\n\n# Run tests\nmake test\n# or\nmake watch-test\n```\n\n## 👤 Contributors\n\n![Contributors](https://contrib.rocks/image?repo=samber/lo)\n\n## 💫 Show your support\n\nGive a ⭐️ if this project helped you!\n\n[![GitHub Sponsors](https://img.shields.io/github/sponsors/samber?style=for-the-badge)](https://github.com/sponsors/samber)\n\n## 📝 License\n\nCopyright © 2022 [Samuel Berthe](https://github.com/samber).\n\nThis project is under [MIT](./LICENSE) license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamber%2Flo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsamber%2Flo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsamber%2Flo/lists"}