Awesome Go

lo

CategoryUtilities
SubcategoryUtilities
Stars21,441

A Lodash like Go library based on Go 1.18+ Generics (map, filter, contains, find...)

About lo

This large README is shown as a plain-text preview. Read the full README at the source


# lo - Iterate over slices, maps, channels...

[![tag](https://img.shields.io/github/tag/samber/lo.svg)](https://github.com/samber/lo/releases)
![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.18-%23007d9c)
[![GoDoc](https://godoc.org/github.com/samber/lo?status.svg)](https://pkg.go.dev/github.com/samber/lo)
![Build Status](https://github.com/samber/lo/actions/workflows/test.yml/badge.svg)
[![Go report](https://goreportcard.com/badge/github.com/samber/lo)](https://goreportcard.com/report/github.com/samber/lo)
[![Coverage](https://img.shields.io/codecov/c/github/samber/lo)](https://codecov.io/gh/samber/lo)
[![Contributors](https://img.shields.io/github/contributors/samber/lo)](https://github.com/samber/lo/graphs/contributors)
[![License](https://img.shields.io/github/license/samber/lo)](./LICENSE)

✨ **`samber/lo` is a Lodash-style Go library based on Go 1.18+ Generics.**

A 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.

5 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.

**See also:**

- [samber/ro](https://github.com/samber/ro): Reactive Programming for Go: declarative and composable API for event-driven applications
- [samber/do](https://github.com/samber/do): A dependency injection toolkit based on Go 1.18+ Generics
- [samber/mo](https://github.com/samber/mo): Monads based on Go 1.18+ Generics (Option, Result, Either...)
- [samber/cc-skills-golang](https://github.com/samber/cc-skills-golang): AI Agent Skills for Golang

What makes it different from **samber/ro**?
- lo: synchronous helpers across finite sequences (maps, slices...)
- ro: processing of infinite data streams for event-driven scenarios

**Why this name?**

I wanted a **short name**, similar to "Lodash", and no Go package uses this name.

![lo](docs/static/img/logo-full.png)

## šŸš€ Install

```sh
go get github.com/samber/lo@v1

# AI Agent Skill
npx skills add https://github.com/samber/cc-skills-golang --skill golang-samber-lo
```

This library is v1 and follows SemVer strictly.

No breaking changes will be made to exported APIs before v2.0.0, except for experimental packages under `exp/`.

This library has no dependencies outside the Go standard library.

## šŸ’” Usage

You can import `lo` using:

```go
import (
    "github.com/samber/lo"
    lop "github.com/samber/lo/parallel"
    lom "github.com/samber/lo/mutable"
    loi "github.com/samber/lo/it"
)
```

Then use one of the helpers below:

```go
names := lo.Uniq([]string{"Samuel", "John", "Samuel"})
// []string{"Samuel", "John"}
```

### Tips for lazy developers

I cannot recommend it, but in case you are too lazy for repeating `lo.` everywhere, you can import the entire library into the namespace.

```go
import (
    . "github.com/samber/lo"
)
```

I take no responsibility for this junk. 😁 šŸ’©

## 🤠 Spec

GoDoc: [godoc.org/github.com/samber/lo](https://godoc.org/github.com/samber/lo)

Documentation: [lo.samber.dev](https://lo.samber.dev/docs/about)

Supported helpers for slices:

- [Filter](#filter)
- [Map](#map)
- [UniqMap](#uniqmap)
- [FilterMap](#filtermap)
- [FlatMap](#flatmap)
- [Reduce](#reduce)
- [ReduceRight](#reduceright)
- [ForEach](#foreach)
- [ForEachWhile](#foreachwhile)
- [ForEachErr](#foreacherr)
- [Times](#times)
- [Uniq](#uniq)
- [UniqBy](#uniqby)
- [IsUniq](#isuniq)
- [IsUniqBy](#isuniqby)
- [GroupBy](#groupby)
- [GroupByMap](#groupbymap)
- [Chunk](#chunk)
- [Window](#window)
- [Sliding](#sliding)
- [PartitionBy](#partitionby)
- [Flatten](#flatten)
- [Concat](#concat)
- [Interleave](#interleave)
- [Shuffle](#shuffle)
- [Reverse](#reverse)
- [Fill](#fill)
- [Repeat](#repeat)
- [RepeatBy](#repeatby)
- [KeyBy](#keyby)
- [SliceToMap / Associate](#slicetomap-alias-associate)
- [FilterSliceToMap](#filterslicetomap)
- [Keyify](#keyify)
- [Take](#take)
- [TakeWhile](#takewhile)
- [TakeFilter](#takefilter)
- [Drop](#drop)
- [DropRight](#dropright)
- [DropWhile](#dropwhile)
- [DropRightWhile](#droprightwhile)
- [DropByIndex](#DropByIndex)
- [Reject](#reject)
- [RejectMap](#rejectmap)
- [FilterReject](#filterreject)
- [Count](#count)
- [CountBy](#countby)
- [CountValues](#countvalues)
- [CountValuesBy](#countvaluesby)
- [Subset](#subset)
- [Slice](#slice)
- [Replace](#replace)
- [ReplaceAll](#replaceall)
- [Clone](#clone)
- [Compact](#compact)
- [IsSorted](#issorted)
- [IsSortedBy](#issortedby)
- [Splice](#Splice)
- [Cut](#Cut)
- [CutPrefix](#CutPrefix)
- [CutSuffix](#CutSuffix)
- [Trim](#Trim)
- [TrimLeft](#TrimLeft)
- [TrimPrefix](#TrimPrefix)
- [TrimRight](#TrimRight)
- [TrimSuffix](#TrimSuffix)

Supported helpers for maps:

- [Keys](#keys)
- [UniqKeys](#uniqkeys)
- [HasKey](#haskey)
- [ValueOr](#valueor)
- [Values](#values)
- [UniqValues](#uniqvalues)
- [PickBy](#pickby)
- [PickByKeys](#pickbykeys)
- [PickByValues](#pickbyvalues)
- [OmitBy](#omitby)
- [OmitByKeys](#omitbykeys)
- [OmitByValues](#omitbyvalues)
- [Entries / ToPairs](#entries-alias-topairs)
- [FromEntries / FromPairs](#fromentries-alias-frompairs)
- [Invert](#invert)
- [Assign (merge of maps)](#assign)
- [ChunkEntries](#chunkentries)
- [MapKeys](#mapkeys)
- [MapValues](#mapvalues)
- [MapEntries](#mapentries)
- [MapToSlice](#maptoslice)
- [FilterMapToSlice](#FilterMapToSlice)
- [FilterKeys](#FilterKeys)
- [FilterValues](#FilterValues)

Supported math helpers:

- [Range / RangeFrom / RangeWithSteps](#range--rangefrom--rangewithsteps)
- [Clamp](#clamp)
- [Sum](#sum)
- [SumBy](#sumby)
- [Product](#product)
- [ProductBy](#productby)
- [Mean](#mean)
- [MeanBy](#meanby)
- [Mode](#mode)

Supported helpers for strings:

- [RandomString](#randomstring)
- [Substring](#substring)
- [ChunkString](#chunkstring)
- [RuneLength](#runelength)
- [PascalCase](#pascalcase)
- [CamelCase](#camelcase)
- [KebabCase](#kebabcase)
- [SnakeCase](#snakecase)
- [Words](#words)
- [Capitalize](#capitalize)
- [Ellipsis](#ellipsis)

Supported helpers for tuples:

- [T2 -> T9](#t2---t9)
- [Unpack2 -> Unpack9](#unpack2---unpack9)
- [Zip2 -> Zip9](#zip2---zip9)
- [ZipBy2 -> ZipBy9](#zipby2---zipby9)
- [Unzip2 -> Unzip9](#unzip2---unzip9)
- [UnzipBy2 -> UnzipBy9](#unzipby2---unzipby9)
- [CrossJoin2 -> CrossJoin2](#crossjoin2---crossjoin9)
- [CrossJoinBy2 -> CrossJoinBy2](#crossjoinby2---crossjoinby9)

Supported helpers for time and duration:

- [Duration](#duration)
- [Duration0 -> Duration10](#duration0---duration10)

Supported helpers for channels:

- [ChannelDispatcher](#channeldispatcher)
- [SliceToChannel](#slicetochannel)
- [ChannelToSlice](#channeltoslice)
- [Generator](#generator)
- [Buffer](#buffer)
- [BufferWithContext](#bufferwithcontext)
- [BufferWithTimeout](#bufferwithtimeout)
- [FanIn](#fanin)
- [FanOut](#fanout)

Supported intersection helpers:

- [Contains](#contains)
- [ContainsBy](#containsby)
- [Every](#every)
- [EveryBy](#everyby)
- [Some](#some)
- [SomeBy](#someby)
- [None](#none)
- [NoneBy](#noneby)
- [Intersect](#intersect)
- [IntersectBy](#intersectby)
- [Difference](#difference)
- [Union](#union)
- [UnionBy](#unionby)
- [UnionByErr](#unionbyerr)
- [Without](#without)
- [WithoutBy](#withoutby)
- [WithoutEmpty](#withoutempty)
- [WithoutNth](#withoutnth)
- [ElementsMatch](#ElementsMatch)
- [ElementsMatchBy](#ElementsMatchBy)

Supported search helpers:

- [IndexOf](#indexof)
- [LastIndexOf](#lastindexof)
- [HasPrefix](#hasprefix)
- [HasSuffix](#hassuffix)
- [Find](#find)
- [FindIndexOf](#findindexof)
- [FindLastIndexOf](#findlastindexof)
- [FindOrElse](#findorelse)
- [FindKey](#findkey)
- [FindKeyBy](#findkeyby)
- [FindUniques](#finduniques)
- [FindUniquesBy](#finduniquesby)
- [FindDuplicates](#findduplicates)
- [FindDuplicatesBy](#findduplicatesby)
- [Min](#min)
- [MinIndex](#minindex)
- [MinBy](#minby)
- [MinIndexBy](#minindexby)
- [Earliest](#earliest)
- [EarliestBy](#earliestby)
- [Max](#max)
- [MaxIndex](#maxindex)
- [MaxBy](#maxby)
- [MaxIndexBy](#maxindexby)
- [Latest](#latest)
- [LatestBy](#latestby)
- [First](#first)
- [FirstOrEmpty](#FirstOrEmpty)
- [FirstOr](#FirstOr)
- [Last](#last)
- [LastOrEmpty](#LastOrEmpty)
- [LastOr](#LastOr)
- [Nth](#nth)
- [NthOr](#nthor)
- [NthOrEmpty](#nthorempty)
- [Sample](#sample)
- [SampleBy](#sampleby)
- [Samples](#samples)
- [SamplesBy](#samplesby)

Conditional helpers:

- [Ternary](#ternary)
- [TernaryF](#ternaryf)
- [If / ElseIf / Else](#if--elseif--else)
- [Switch / Case / Default](#switch--case--default)

Type manipulation helpers:

- [IsNil](#isnil)
- [IsNotNil](#isnotnil)
- [ToPtr](#toptr)
- [Nil](#nil)
- [EmptyableToPtr](#emptyabletoptr)
- [FromPtr](#fromptr)
- [FromPtrOr](#fromptror)
- [ToSlicePtr](#tosliceptr)
- [FromSlicePtr](#fromsliceptr)
- [FromSlicePtrOr](#fromsliceptror)
- [ToAnySlice](#toanyslice)
- [FromAnySlice](#fromanyslice)
- [Empty](#empty)
- [IsEmpty](#isempty)
- [IsNotEmpty](#isnotempty)
- [Coalesce](#coalesce)
- [CoalesceOrEmpty](#coalesceorempty)
- [CoalesceSlice](#coalesceslice)
- [CoalesceSliceOrEmpty](#coalescesliceorempty)
- [CoalesceMap](#coalescemap)
- [CoalesceMapOrEmpty](#coalescemaporempty)

Function helpers:

- [Partial](#partial)
- [Partial2 -> Partial5](#partial2---partial5)

Concurrency helpers:

- [Attempt](#attempt)
- [AttemptWhile](#attemptwhile)
- [AttemptWithDelay](#attemptwithdelay)
- [AttemptWhileWithDelay](#attemptwhilewithdelay)
- [Debounce](#debounce)
- [DebounceBy](#debounceby)
- [Throttle](#throttle)
- [ThrottleWithCount](#throttle)
- [ThrottleBy](#throttle)
- [ThrottleByWithCount](#throttle)
- [Synchronize](#synchronize)
- [Async](#async)
- [Async{0->6}](#async0-6)
- [Transaction](#transaction)
- [WaitFor](#waitfor)
- [WaitForWithContext](#waitforwithcontext)

Error handling:

- [Validate](#validate)
- [Must](#must)
- [Try](#try)
- [Try1 -> Try6](#try0-6)
- [TryOr](#tryor)
- [TryOr1 -> TryOr6](#tryor0-6)
- [TryCatch](#trycatch)
- [TryWithErrorValue](#trywitherrorvalue)
- [TryCatchWithErrorValue](#trycatchwitherrorvalue)
- [ErrorsAs](#errorsas)
- [Assert](#assert)
- [Assertf](#assertf)

Constraints:

- Clonable

### Filter

Iterates over a collection and returns a slice of all the elements the predicate function returns `true` for.

```go
result := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {
    return x%2 == 0
})
// []int{2, 4}
```
[[play](https://go.dev/play/p/Apjg3WeSi7K)]
```go
// Use FilterErr when the predicate can return an error
result, err := lo.FilterErr([]int{1, 2, 3, 4}, func(x int, _ int) (bool, error) {
    if x == 3 {
        return false, fmt.Errorf("number 3 is not allowed")
    }
    return x%2 == 0, nil
})
// []int(nil), error("number 3 is not allowed")
```

[[play](https://go.dev/play/p/Apjg3WeSi7K)]

Mutable: like `lo.Filter()`, but the slice is updated in place.

```go
import lom "github.com/samber/lo/mutable"

list := []int{1, 2, 3, 4}
newList := lom.Filter(list, func(x int) bool {
    return x%2 == 0
})

list
// []int{2, 4, 3, 4}

newList
// []int{2, 4}
```
[[play](https://go.dev/play/p/i-ZbmM2exxi)]

### Map

Manipulates a slice of one type and transforms it into a slice of another type:

```go
import "github.com/samber/lo"

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

```go
// Use MapErr when the transform function can return an error
result2, err := lo.MapErr([]int{1, 2, 3, 4}, func(x int, _ int) (string, error) {
    if x == 3 {
        return "", fmt.Errorf("number 3 is not allowed")
    }
    return strconv.Itoa(x), nil
})
// []string(nil), error("number 3 is not allowed")
```

[[play](https://go.dev/play/p/zQOU_WkotqD)]

Parallel processing: like `lo.Map()`, but the transform function is called in a goroutine. Results are returned in the same order.

```go
import lop "github.com/samber/lo/parallel"

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

[[play](https://go.dev/play/p/2t3G5xrx9_G)]

Mutable: like `lo.Map()`, but the slice is updated in place.

```go
import lom "github.com/samber/lo/mutable"

list := []int{1, 2, 3, 4}
lom.Map(list, func(x int) int {
    return x*2
})
// []int{2, 4, 6, 8}
```

[[play](https://go.dev/play/p/_OFK7SPV8Qd)]

### UniqMap

Manipulates a slice and transforms it to a slice of another type with unique values.

```go
type User struct {
    Name string
    Age  int
}
users := []User{{Name: "Alex", Age: 10}, {Name: "Alex", Age: 12}, {Name: "Bob", Age: 11}, {Name: "Alice", Age: 20}}

names := lo.UniqMap(users, func(u User, index int) string {
    return u.Name
})
// []string{"Alex", "Bob", "Alice"}
```

[[play](https://go.dev/play/p/WDHGpgbXQr2)]

### FilterMap

Returns a slice obtained after both filtering and mapping using the given callback function.

The callback function should return two values: the result of the mapping operation and whether the result element should be included or not.

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

[[play](https://go.dev/play/p/dF88-9oUOq4)]

### FlatMap

Manipulates 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.

```go
lo.FlatMap([]int64{0, 1, 2}, func(x int64, _ int) []string {
    return []string{
        strconv.FormatInt(x, 10),
        strconv.FormatInt(x, 10),
    }
})
// []string{"0", "0", "1", "1", "2", "2"}
```

```go
// Use FlatMapErr when the transform function can return an error
result, err := lo.FlatMapErr([]int64{0, 1, 2, 3}, func(x int64, _ int) ([]string, error) {
    if x == 2 {
        return nil, fmt.Errorf("number 2 is not allowed")
    }
    return []string{strconv.FormatInt(x, 10), strconv.FormatInt(x, 10)}, nil
})
// []string(nil), error("number 2 is not allowed")
```

[[play](https://go.dev/play/p/RNUIfIbrVvN)]

### Reduce

Reduces 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.

```go
sum := lo.Reduce([]int{1, 2, 3, 4}, func(agg int, item int, _ int) int {
    return agg + item
}, 0)
// 10
```

```go
// Use ReduceErr when the accumulator function can return an error
result, err := lo.ReduceErr([]int{1, 2, 3, 4}, func(agg int, item int, _ int) (int, error) {
    if item == 3 {
        return 0, fmt.Errorf("number 3 is not allowed")
    }
    return agg + item, nil
}, 0)
// 0, error("number 3 is not allowed")
```

[[play](https://go.dev/play/p/-3iKnq-zgaL)]

### ReduceRight

Like `lo.Reduce` except that it iterates over elements of collection from right to left.

```go
result := lo.ReduceRight([][]int{{0, 1}, {2, 3}, {4, 5}}, func(agg []int, item []int, _ int) []int {
    return append(agg, item...)
}, []int{})
// []int{4, 5, 2, 3, 0, 1}
```

```go
// Use ReduceRightErr when the accumulator function can return an error
result, err := lo.ReduceRightErr([]int{1, 2, 3, 4}, func(agg int, item int, _ int) (int, error) {
    if item == 2 {
        return 0, fmt.Errorf("number 2 is not allowed")
    }
    return agg + item, nil
}, 0)
// 0, error("number 2 is not allowed")
```

[[play](https://go.dev/play/p/-RZKja4RvQa)]

### ForEach

Iterates over elements of a collection and invokes the function over each element.

```go
import "github.com/samber/lo"

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

[[play](https://go.dev/play/p/oofyiUPRf8t)]

Parallel processing: like `lo.ForEach()`, but the callback is called as a goroutine.

```go
import lop "github.com/samber/lo/parallel"

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

### ForEachWhile

Iterates over collection elements and invokes iteratee for each element collection return value decide to continue or break, like do while().

```go
list := []int64{1, 2, -42, 4}

lo.ForEachWhile(list, func(x int64, _ int) bool {
	if x < 0 {
		return false
	}
	fmt.Println(x)
	return true
})
// 1
// 2
```

[[play](https://go.dev/play/p/QnLGt35tnow)]

### ForEachErr

Iterates over elements of a collection, invokes the iteratee for each element, and returns the first error returned by the iteratee. Iteration stops on the first error.

```go
import "github.com/samber/lo"

list := []int64{1, 2, -42, 4}

err := lo.ForEachErr(list, func(x int64, _ int) error {
	if x < 0 {
		return fmt.Errorf("%d is not allowed", x)
	}
	fmt.Println(x)
	return nil
})
// 1
// 2
// err: "-42 is not allowed"
```

### Times

Times invokes the iteratee n times, returning a slice of the results of each invocation. The iteratee is invoked with index as argument.

```go
import "github.com/samber/lo"

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

[[play](https://go.dev/play/p/vgQj3Glr6lT)]

Parallel processing: like `lo.Times()`, but callback is called in goroutine.

```go
import lop "github.com/samber/lo/parallel"

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

### Uniq

Returns a duplicate-free version of a slice, 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 slice.

```go
uniqValues := lo.Uniq([]int{1, 2, 2, 1})
// []int{1, 2}
```

[[play](https://go.dev/play/p/DTzbeXZ6iEN)]

### UniqBy

Returns a duplicate-free version of a slice, 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 slice. It accepts `iteratee` which is invoked for each element in the slice to generate the criterion by which uniqueness is computed.

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

```go
// Use UniqByErr when the iteratee function can return an error
result, err := lo.UniqByErr([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, error) {
    if i == 3 {
        return 0, fmt.Errorf("number 3 is not allowed")
    }
    return i % 3, nil
})
// []int(nil), error("number 3 is not allowed")
```

[[play](https://go.dev/play/p/g42Z3QSb53u)]

### IsUniq

Returns true if all elements in the slice are unique, false otherwise. Returns true for nil and empty slices.

```go
lo.IsUniq([]int{1, 2, 3})
// true

lo.IsUniq([]int{1, 2, 1})
// false
```

### IsUniqBy

Returns true if all elements in the slice are unique based on a custom criterion. It accepts `iteratee` which is invoked for each element in the slice to generate the criterion by which uniqueness is computed. Returns true for nil and empty slices.

```go
type User struct {
    ID   int
    Name string
}

users := []User{{ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}, {ID: 1, Name: "Charlie"}}

lo.IsUniqBy(users, func(u User) int { return u.ID })
// false

lo.IsUniqBy(users, func(u User) string { return u.Name })
// true
```

### GroupBy

Returns an object composed of keys generated from the results of running each element of collection through iteratee.

```go
import lo "github.com/samber/lo"

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

```go
// Use GroupByErr when the iteratee function can return an error

Frequently Asked Questions

What is lo?

lo is a Utilities library for the Go programming language. A Lodash like Go library based on Go 1.18+ Generics (map, filter, contains, find...)

How many GitHub stars does lo have?

lo has 21,441 GitHub stars in the directory's latest synchronization.

How do I install lo?

Install lo with the Go module system using `go get samber/lo`. Check the repository for the current installation instructions.

What category does lo belong to?

lo is listed under Utilities, specifically Utilities.

← Back to Utilities