Golang Samber Lo
Samuel BertheCommunityCreated by samberDistributed by Samuel Berthe
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The samber/lo library brings Lodash-inspired functional programming patterns to Go. It provides a comprehensive suite of type-safe, generic functions for manipulating slices, maps, channels, and strings. By offering immutable-by-default operations, it helps developers reduce boilerplate code associated with manual loops while maintaining type safety and avoiding reflection. The library includes specialized sub-packages for parallel processing (lop), in-place mutation (lom), and lazy evaluation (loi), allowing developers to scale from simple data transformations to performance-optimized pipelines.
What it does
- Provides 500+ generic functions for common data manipulation tasks like Map, Filter, Reduce, and GroupBy.
- Supports immutable operations by default to ensure safer concurrent reads.
- Includes specialized packages for parallel execution (lop) and in-place mutation (lom) for performance tuning.
- Offers lazy iterator support (loi) for Go 1.23+ to minimize memory allocations in transformation chains.
- Includes error-handling variants for common functions to simplify control flow.
- Maintains zero external dependencies, relying solely on the Go standard library.
When to use it
- When you need to perform complex data transformations on slices or maps that would otherwise require verbose manual loops.
- When you want to adopt a more declarative, functional coding style in your Go projects.
- When you need to handle errors during collection processing using built-in error-propagating variants.
- When profiling indicates that standard library approaches are insufficient and you need parallel or lazy evaluation strategies.
- When you need to perform set operations or complex grouping tasks efficiently.
Advantages
- Eliminates boilerplate code and reduces the surface area for logic errors in loops.
- Type-safe generics ensure compile-time checking without the overhead of interface boxing.
- Composable design allows chaining operations cleanly.
- Progressive complexity allows starting with simple helpers and upgrading to performance-oriented variants only when necessary.
- Zero dependencies minimize supply chain risks.
Usage examples
- “Use lo.Map to extract names from a slice of user structs.”
- The agent generates a concise lo.Map call, replacing a manual for-loop and make-slice initialization.
- “How do I group tasks by status using samber/lo?”
- The agent provides a snippet using lo.GroupBy, demonstrating how to transform a slice into a map of slices.
- “I have a large slice and need to filter and map it without extra allocations.”
- The agent suggests using the loi (lazy iterator) package to chain operations lazily, avoiding intermediate slice allocations.
- “My code is failing on the first error during a map operation; how can I fix this?”
- The agent recommends switching to lo.MapErr, which stops processing immediately upon encountering an error and returns the error value.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/samber/golang-samber-lo/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip golang-samber-lo.zip -d .agents/skills
Documentation by samber
Persona: You are a Go engineer who prefers declarative collection transforms over manual loops. You reach for lo to eliminate boilerplate, but you know when the stdlib is enough and when to upgrade to lop, lom, or loi.
samber/lo — Functional Utilities for Go
Lodash-inspired, generics-first utility library with 500+ type-safe helpers for slices, maps, strings, math, channels, tuples, and concurrency. Zero external dependencies. Immutable by default.
Official Resources:
This skill is not exhaustive — refer to library documentation and code examples for more information:
For Go package docs, symbols, versions, importers, and known vulnerabilities, → See
samber/cc-skills-golang@golang-pkg-go-devskill (godig), preferred over Context7 for Go package facts.To navigate this library's usage in your own code (definitions, call sites, diagnostics), → See
samber/cc-skills-golang@golang-goplsskill (gopls).Context7 remains a fallback for docs not indexed on pkg.go.dev.
Why samber/lo
Go's stdlib slices and maps packages cover ~10 basic helpers (sort, contains, keys). Everything else — Map, Filter, Reduce, GroupBy, Chunk, Flatten, Zip — requires manual for-loops. lo fills this gap:
Type-safe generics — no
interface{}casts, no reflection, compile-time checking, no interface boxing overheadImmutable by default — returns new collections, safe for concurrent reads, easier to reason about
Composable — functions take and return slices/maps, so they chain without wrapper types
Zero dependencies — only Go stdlib, no transitive dependency risk
Progressive complexity — start with
lo, upgrade tolop/lom/loionly when profiling demands itError variants — most functions have
Errsuffixes (MapErr,FilterErr,ReduceErr) that stop on first error
Installation
go get github.com/samber/lo| Package | Import | Alias | Go version |
|---|---|---|---|
| Core (immutable) | github.com/samber/lo | lo | 1.18+ |
| Parallel | github.com/samber/lo/parallel | lop | 1.18+ |
| Mutable | github.com/samber/lo/mutable | lom | 1.18+ |
| Iterator | github.com/samber/lo/it | loi | 1.23+ |
| SIMD (experimental) | github.com/samber/lo/exp/simd | — | 1.25+ (amd64 only) |
Choose the Right Package
Start with lo. Move to other packages only when profiling shows a bottleneck or when lazy evaluation is explicitly needed.
| Package | Use when | Trade-off |
|---|---|---|
lo | Default for all transforms | Allocates new collections (safe, predictable) |
lop | CPU-bound work on large datasets (1000+ items) | Goroutine overhead; not for I/O or small slices |
lom | Hot path confirmed by pprof -alloc_objects | Mutates input — caller must understand side effects |
loi | Large datasets with chained transforms (Go 1.23+) | Lazy evaluation saves memory but adds iterator complexity |
simd | Numeric bulk ops after benchmarking (experimental) | Unstable API, may break between versions |
Key rules:
lopis for CPU parallelism, not I/O concurrency — for I/O fan-out, useerrgroupinsteadlombreaks immutability — only use when allocation pressure is measured, never assumedloieliminates intermediate allocations in chains likeMap → Filter → Takeby evaluating lazilyFor reactive/streaming pipelines over infinite event streams, → see
samber/cc-skills-golang@golang-samber-roskill +samber/ropackage
For detailed package comparison and decision flowchart, see Package Guide.
Core Patterns
Transform a slice
// ✓ lo — declarative, type-safe
names := lo.Map(users, func(u User, _ int) string {
return u.Name
})
// ✗ Manual — boilerplate, error-prone
names := make([]string, 0, len(users))
for _, u := range users {
names = append(names, u.Name)
}Filter + Reduce
total := lo.Reduce(
lo.Filter(orders, func(o Order, _ int) bool {
return o.Status == "paid"
}),
func(sum float64, o Order, _ int) float64 {
return sum + o.Amount
},
0,
)GroupBy
byStatus := lo.GroupBy(tasks, func(t Task, _ int) string {
return t.Status
})
// map[string][]Task{"open": [...], "closed": [...]}Error variant — stop on first error
results, err := lo.MapErr(urls, func(url string, _ int) (Response, error) {
return http.Get(url)
})Common Mistakes
| Mistake | Why it fails | Fix |
|---|---|---|
Using lo.Contains when slices.Contains exists | Unnecessary dependency for a stdlib-covered op | Prefer slices.Contains/slices.Sort since Go 1.21+ and slices.Collect(maps.Keys(m)) since Go 1.23+ when a key slice is needed |
Using lop.Map on 10 items | Goroutine creation overhead exceeds transform cost | Use lo.Map — lop benefits start at ~1000+ items for CPU-bound work |
Assuming lo.Filter modifies the input | lo is immutable by default — it returns a new slice | Use lom.Filter if you explicitly need in-place mutation |
Using lo.Must in production code paths | Must panics on error — fine in tests and init, dangerous in request handlers | Use the non-Must variant and handle the error |
| Chaining many eager transforms on large data | Each step allocates an intermediate slice | Use loi (lazy iterators) to avoid intermediate allocations |
Best Practices
Prefer stdlib when available —
slices.Containsandslices.Sort(Go 1.21+) carry no dependency;maps.Keysis Go 1.23+ and returns an iterator, so useslices.Collect(maps.Keys(m))when you need a slice. Uselofor transforms the stdlib doesn't offer (Map, Filter, Reduce, GroupBy, Chunk, Flatten)Compose lo functions — chain
lo.Filter→lo.Map→lo.GroupByinstead of writing nested loops. Each function is a building blockProfile before optimizing — switch from
lotolom/loponly aftergo tool pprofconfirms allocation or CPU as the bottleneckUse error variants — prefer
lo.MapErroverlo.Map+ manual error collection. Error variants stop early and propagate cleanlyUse
lo.Mustonly in tests and init — in production, handle errors explicitly
Quick Reference
| Function | What it does |
|---|---|
lo.Map | Transform each element |
lo.Filter / lo.Reject | Keep / remove elements matching predicate |
lo.Reduce | Fold elements into a single value |
lo.ForEach | Side-effect iteration |
lo.GroupBy | Group elements by key |
lo.Chunk | Split into fixed-size batches |
lo.Flatten | Flatten nested slices one level |
lo.Uniq / lo.UniqBy | Remove duplicates |
lo.Find / lo.FindOrElse | First match or default |
lo.Contains / lo.Every / lo.Some | Membership tests |
lo.Keys / lo.Values | Extract map keys or values |
lo.PickBy / lo.OmitBy | Filter map entries |
lo.Zip2 / lo.Unzip2 | Pair/unpair two slices |
lo.Range / lo.RangeFrom | Generate number sequences |
lo.Ternary / lo.If | Inline conditionals |
lo.ToPtr / lo.FromPtr | Pointer helpers |
lo.Must / lo.Try | Panic-on-error / recover-as-bool |
lo.Async / lo.Attempt | Async execution / retry with backoff |
lo.Debounce / lo.Throttle | Rate limiting |
lo.ChannelDispatcher | Fan-out to multiple channels |
For the complete function catalog (300+ functions), see API Reference.
For composition patterns, stdlib interop, and iterator pipelines, see Advanced Patterns.
If you encounter a bug or unexpected behavior in samber/lo, open an issue at github.com/samber/lo/issues.
Cross-References
→ See
samber/cc-skills-golang@golang-samber-roskill for reactive/streaming pipelines over infinite event streams (samber/ropackage)→ See
samber/cc-skills-golang@golang-samber-moskill for monadic types (Option, Result, Either) that compose with lo transforms→ See
samber/cc-skills-golang@golang-data-structuresskill for choosing the right underlying data structure→ See
samber/cc-skills-golang@golang-performanceskill for profiling methodology before switching tolom/lop
Files
- ATTRIBUTION.mdAdded by AutoGineer905 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md9.6 KiB
- evals/evals.json19.0 KiB
- references/advanced-patterns.md5.0 KiB
- references/api-reference.md18.5 KiB
- references/package-guide.md6.7 KiB
references/package-guide.md
Package Guide
samber/lo ships five packages. Each serves a different performance/ergonomics trade-off.
Table of Contents
Import Paths and Aliases
lo— Core (Immutable)lo/parallel(lop) — Concurrent Transformslo/mutable(lom) — In-Place Mutationslo/it(loi) — Lazy Iteratorslo/exp/simd— Experimental SIMDDecision Flowchart
Comparison Table
Import Paths and Aliases
import (
"github.com/samber/lo" // lo — core, immutable
"github.com/samber/lo/parallel" // lop — concurrent transforms
"github.com/samber/lo/mutable" // lom — in-place mutations
"github.com/samber/lo/it" // loi — lazy iterators (Go 1.23+)
"github.com/samber/lo/exp/simd" // experimental SIMD
)lo — Core (Immutable)
The default package. 300+ functions that return new collections without modifying inputs.
Mental model: Functional transforms like JavaScript's Array.prototype.map/filter/reduce, but type-safe via generics.
Characteristics:
Every function allocates a new result slice/map — the input is never modified
Safe for concurrent reads on the input while transforms run
Composable: output of one function feeds directly into the next
Use when: Always start here. Only move to other packages when profiling reveals a measured bottleneck.
// Immutable — users slice is untouched
active := lo.Filter(users, func(u User, _ int) bool {
return u.Active
})lo/parallel (lop) — Concurrent Transforms
Parallel variants of core functions. Each element is processed in a separate goroutine with automatic worker pooling.
Available functions: Map, ForEach, Times, GroupBy, PartitionBy
Characteristics:
Results preserve original order despite concurrent execution
Internal goroutine pool manages concurrency (not configurable via API — one goroutine per element)
Synchronization via
sync.WaitGroup
Use when:
CPU-bound transforms on large datasets (~1000+ items)
Transform function is expensive (parsing, hashing, computing)
Order must be preserved
Do NOT use when:
Small datasets (<100 items) — goroutine creation overhead exceeds benefit
I/O-bound work (HTTP calls, DB queries) — use
errgroupwith context cancellation insteadTransform function is trivial (field access, type cast) —
lo.Mapis faster
// CPU-heavy: parse 10k JSON documents in parallel
parsed := lop.Map(rawDocs, func(doc []byte, _ int) *Document {
return parseDocument(doc) // expensive operation
})Diagnose: go tool pprof -cpu — if transform function dominates CPU profile and dataset is large, lop helps.
lo/mutable (lom) — In-Place Mutations
Modify the original slice directly. Zero allocation overhead.
Available functions: Filter, Map, Shuffle, Reverse, Replace
Characteristics:
Modifies the input slice — callers must expect side effects
lom.Filtershortens the slice (removes non-matching elements in-place)lom.Maptransforms elements in-place (preserves length)Uses Fisher-Yates for
ShuffleNot safe for concurrent access to the source slice
Use when:
go tool pprof -alloc_objectsconfirms allocation pressure fromlo.Filter/lo.MapWorking with very large slices where GC pressure is measurable
You explicitly want to modify the source and won't need the original
Do NOT use when:
Multiple goroutines read the same slice
You need the original data after the transform
Code readability matters more than micro-optimization
// In-place filter — modifies 'items' directly
items = lom.Filter(items, func(item Item, _ int) bool {
return item.Price > 0
})Diagnose: 1- go tool pprof -alloc_objects — find which lo.* calls allocate the most 2- go build -gcflags="-m" — check if result slices escape to heap
lo/it (loi) — Lazy Iterators
Go 1.23+ iterator support with lazy evaluation. Transforms are deferred until consumed — no intermediate slices allocated.
Characteristics:
Uses
range-over-func (Go 1.23+)Composable pipelines:
loi.Map→loi.Filter→loi.Takeruns as a single passNo intermediate slice allocations between pipeline stages
Modules:
channel,find,intersect,map,math,seq,string,tuples,type_manipulation
Use when:
Chaining 3+ transforms on large datasets — eliminates intermediate allocations
Processing sequences where you only need a subset (lazy
Take/TakeWhile)Building composable pipelines with range-over-func
Do NOT use when:
Go version < 1.23
Simple single-step transforms —
lo.Mapis clearer and has negligible overheadYou need random access to intermediate results
// Lazy pipeline — no intermediate slices allocated
for name := range loi.Map(
loi.Filter(users, func(u User) bool { return u.Active }),
func(u User) string { return u.Name },
) {
fmt.Println(name)
}lo/exp/simd — Experimental SIMD
SIMD (Single Instruction Multiple Data) optimized operations for numeric types on amd64.
Use when: Bulk numeric operations after benchmarking confirms the bottleneck. Very specialized.
Warning: This package is experimental and not covered by semver stability guarantees, so its API may break between minor versions. Do not use in production without version pinning.
Decision Flowchart
Start with lo.Map/Filter/Reduce (immutable, safe)
│
├─ Profiler shows allocation pressure?
│ └─ Yes → Switch specific calls to lom (mutable)
│
├─ Profiler shows CPU-bound transform is slow?
│ └─ Yes + large dataset → Switch to lop (parallel)
│
├─ Chaining 3+ transforms with intermediate allocations?
│ └─ Yes + Go 1.23+ → Switch to loi (lazy iterators)
│
└─ Need reactive/streaming over infinite events?
└─ Yes → Use samber/ro instead (different library)Comparison Table
| Aspect | lo | lop | lom | loi | simd |
|---|---|---|---|---|---|
| Allocations | New slice/map | New slice/map | Zero (in-place) | Zero (lazy) | Varies |
| Goroutines | None | 1 per element | None | None | None |
| Order preserved | Yes | Yes | Yes | Yes | Yes |
| Input modified | No | No | Yes | No | Varies |
| Concurrent-safe | Read-safe | Read-safe | Not safe | Read-safe | Varies |
| API stability | Stable | Stable | Stable | Stable | Experimental |
| Go version | 1.18+ | 1.18+ | 1.18+ | 1.23+ | 1.25+ |
Requirements and compatibility
Written for: Claude Code, OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Declares allowed tools, Claude Code frontmatter
The author says: Designed for Claude Code, Codex or similar harness, and for projects using Golang.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- samber
- Publisher
- Samuel Berthe (Community)
- Distributed by
- Samuel Berthe
- Source
- samber/cc-skills-golang · skills/golang-samber-lo · commit bb1d43d
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by samber or Samuel Berthe.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download golang-samber-lo.zip