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 — это библиотека утилит для Go в стиле Lodash, построенная на дженериках (generics) из версии 1.18+: десятки вспомогательных функций для фильтрации, преобразования, группировки и свёртки слайсов, карт, строк, каналов и функций.
Это библиотека, которую я импортирую в свой код на Go, или отдельная программа?Это чистая библиотека «импортировал и вызывай»: без бинарника, сервера или конфигов. Вы подключаете её к своему Go-коду и напрямую используете функции вроде lo.Filter или lo.Map.
Как добавить её в проект?Выполните 'go get github.com/samber/lo', импортируйте пакет — и можно вызывать функции. API находится в v1 под строгим SemVer: без breaking-изменений в экспортируемом API до версии v2.0.0, за исключением экспериментальных пакетов внутри 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 есть параллельные варианты, которые запускают callback в горутинах, причём параллельный Map возвращает результаты в том же порядке, что и входной слайс.
Разве стандартная библиотека Go уже не покрывает часть этого?Отчасти: 5–10 функций пересекаются со стандартными пакетами slices и maps, но автор оставил их, потому что библиотека предлагает гораздо больше ценных абстракций поверх.
Есть ли связанные библиотеки от того же автора?Да: samber/ro занимается реактивными бесконечными потоками событий, тогда как lo нацелен на конечные синхронные последовательности; samber/do — это набор инструментов для внедрения зависимостей на дженериках, а samber/mo предоставляет монады вроде Option, Result и Either.
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