lo README

The project's README from github.com/samber/lo. As one markdown file: README.md.

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

tag Go Version GoDoc Build Status Go report Coverage Contributors 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 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:

What makes it different from samber/ro?

Why this name?

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

lo

🚀 Install

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:

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:

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.

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

I take no responsibility for this junk. 😁 💩

🤠 Spec

GoDoc: godoc.org/github.com/samber/lo

Documentation: lo.samber.dev

Supported helpers for slices:

Supported helpers for maps:

Supported math helpers:

Supported helpers for strings:

Supported helpers for tuples:

Supported helpers for time and duration:

Supported helpers for channels:

Supported intersection helpers:

Supported search helpers:

Conditional helpers:

Type manipulation helpers:

Function helpers:

Concurrency helpers:

Error handling:

Constraints:

Filter

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

result := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {
    return x%2 == 0
})
// []int{2, 4}

[play]

// 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]

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

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]

Map

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

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"}
// 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]

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

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]

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

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]

UniqMap

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

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]

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.

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]

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.

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

[play]

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.

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

[play]

ReduceRight

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

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

[play]

ForEach

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

import "github.com/samber/lo"

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

[play]

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

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

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

ForEachWhile

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

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

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

[play]

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.

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.

import "github.com/samber/lo"

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

[play]

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

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

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

Uniq

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.

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

[play]

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.

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

[play]

IsUniq

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

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.

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.

import lo "github.com/samber/lo"

groups := lo.GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
    return i%3
})
// map[int][]int{0: []int{0, 3}, 1: []int{1, 4}, 2: []int{2, 5}}
// Use GroupByErr when the iteratee function can return an error
result, err := lo.GroupByErr([]int{0, 1, 2, 3, 4, 5}, func(i 

lo (samber/lo)

Searching Database…

21.4K ·GitHub
这是什么?samber/lo 是一个 Lodash 风格的 Go 工具库,基于 1.18+ 的泛型构建:提供了几十个辅助函数,用于对切片、映射、字符串、通道和函数进行过滤、映射、分组和归约等操作。
它是一个要导入到我的 Go 代码里的库,还是独立的工具?纯粹即导入即用的库:没有二进制文件、服务器或配置。你把它引入自己的 Go 代码中,直接使用 lo.Filter 或 lo.Map 这类辅助函数即可。
怎么把它加到项目里?运行 'go get github.com/samber/lo',导入后就可以直接调用辅助函数。API 属于严格遵循语义化版本(SemVer)的 v1:在 v2.0.0 之前不会对导出的 API 做破坏性修改,exp/ 目录下的实验性包除外。
需要哪个 Go 版本?会不会引入其他依赖包?需要 Go 1.18 或更新版本,因为每个辅助函数都建立在泛型之上。除此之外它完全自包含:Go 标准库之外没有任何依赖。
有没有办法不用在每个辅助函数前都写 lo.?有:使用点导入(dot-import)可以把整个包拉进你的命名空间,调用辅助函数时就不用加前缀了。不过作者本人不推荐这种写法,还开玩笑说对后果概不负责。
针对哪个操作系统编译有影响吗?基本没有:它是纯 Go 代码,只依赖标准库,所以只要 Go 本身能运行的地方都能编译,从 Linux 服务器到 Windows 和 macOS 机器都没问题。
它包含哪些类型的辅助函数?切片常用函数(Filter、Map、Reduce、GroupBy、Uniq、Shuffle、Chunk),映射辅助函数(Keys、Invert、Assign),字符串大小写转换(SnakeCase、KebabCase),元组 T2-T9 以及 Zip 和 Unzip,通道处理(Buffer、FanIn、FanOut),条件判断(Ternary、Switch),并发控制(Debounce、Throttle、Synchronize、Async),以及错误处理辅助函数(Must、Try、Validate)。
它能并行处理切片吗?可以:Map、ForEach 和 Times 都有并行版本,会在 goroutine 中运行回调,而且并行版 Map 返回的结果顺序和输入切片保持一致。
Go 标准库不是已经覆盖了其中一些功能吗?有一点:大约 5 到 10 个辅助函数和标准库的 slices、maps 包有重叠,但作者还是保留了它们,因为在这个基础上,这个库提供了更多有价值的抽象。
同一作者还有别的相关库吗?有:samber/ro 处理响应式的无限事件流,而 lo 面向有限的有同步序列;samber/do 是基于泛型的依赖注入工具包;samber/mo 则提供 Option、Result、Either 等 monad。
What are similar projects?conc, ramda, effect, bubbletea, fantasy-land, gjson, gqlgen, es-toolkit
Creator
samber
Size
7.8 MB
Stars
21.4K
Age
4y
Commits · 90 Days