Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
G

go-linq

> 编程语言
Open source

.NET LINQ capabilities in Go

3.7K stars0 likes0 views
WebsiteGitHub

About

.NET LINQ capabilities in Go

# go-linq [](https://godoc.org/github.com/ahmetb/go-linq/v5) [](https://github.com/ahmetb/go-linq/actions/workflows/ci.yml) [](https://coveralls.io/github/ahmetb/go-linq?branch=master) [](https://goreportcard.com/report/github.com/ahmetb/go-linq) A powerful language integrated query (LINQ) library for Go. * **Fully type-safe:** `Query[T]` is generic, and type-changing operators like `Select`, `Join` and `GroupBy` are *generic methods* (a Go 1.27 language feature) — no `interface{}`/`any`, no type assertions, no reflection. * **Fast:** 5–15× faster than go-linq v4 and allocation-free per element (see [Performance](#performance)). * Written in vanilla Go, no dependencies! * Complete lazy evaluation with iterator pattern based on the standard `iter.Seq[T]` type. * Safe for concurrent use. * Supports slices, maps, strings, channels, `iter.Seq[T]` iterators, and custom collections. > [!NOTE] > **Why was go-linq rewritten for Go 1.27?** Go now supports > [generic methods](https://go.dev/issue/77273) — methods that declare their > own type parameters, like `func (q Query[T]) Select[TResult any](...)`. > This is the one language feature LINQ-style chaining was missing: without > it, a method could not return a query of a *different* element type, which > forced v1–v4 to erase everything to `any` and patch over it with runtime > reflection. With it, the element type flows through the entire chain at > compile time. The same query, before and after:
go-linq v4 — type-erased go-linq v5 — typed
```go var owners []string FromSlice(cars).Where(func(c any) bool { return c.(Car).year >= 2015 }).Select(func(c any) any { return c.(Car).owner }).ToSlice(&owners) ``` ```go owners := FromSlice(cars).Where(func(c Car) bool { return c.year >= 2015 }).Select(func(c Car) string { return c.owner }).ToSlice() ```
## Requirements go-linq v5 requires **Go 1.27** (currently in release candidate), because it relies on [generic methods](https://go.dev/issue/77273) for type-changing operators. With Go 1.26 or newer installed, the toolchain listed in `go.mod` is downloaded automatically. For older Go versions, use [go-linq v4](https://github.com/ahmetb/go-linq/tree/master), which offers the same operators with an `any`-based API. ## Installation go get github.com/ahmetb/go-linq/v5 ## Quickstart Usage is as easy as chaining methods like: `FromSlice(slice)` `.Where(predicate)` `.Select(selector)` `.Union(data)` Type parameters are fully inferred from your functions: you never write explicit type arguments in a query chain. **Example 1: Find the author who has written the most books** ```go import . "github.com/ahmetb/go-linq/v5" type Book struct { id int title string authors []string } author, ok := FromSlice(books).SelectMany( // make a flat sequence of authors func(book Book) Query[string] { return FromSlice(book.authors) }).GroupBy( // group by author func(author string) string { return author }, func(author string) string { return author }, ).MaxBy( // take the largest group func(group Group[string, string]) int { return len(group.Group) }) // author.Key is the author with the most books ``` **Example 2: Implement a custom method that leaves only values greater than the specified threshold** ```go type MyQuery Query[int] func (q MyQuery) GreaterThan(threshold int) Query[int] { return Query[int](q).Where(func(item int) bool { return item > threshold }) } result := MyQuery(Range(1, 10)).GreaterThan(5).ToSlice() ``` **Example 3: "MapReduce" in a slice of string sentences to list the top 5 most used words** ``` … ``` ## Manual Iteration The `Query[T]` type exposes an `Iterate` field of type `iter.Seq[T]`, which integrates with Go's native iteration style and the `iter`/`slices` standard library packages. **Example 4: Iterate over a query using the standard `for ... range` loop** ```go q := FromSlice([]int{1, 2, 3, 4}) for v := range q.Iterate { fmt.Println(v) } ``` ## Data Source Constructors Each constructor is typed for its specific input, and the element type of the resulting query is inferred from the argument: - `FromSlice` — creates a query from a slice. - `FromMap` — creates a `Query[KeyValue[TKey, TValue]]` from a map. - `FromChannel` — creates a query from a channel. - `FromChannelWithContext` — creates a query from a channel with `Context` support. - `FromString` — creates a `Query[rune]` from a string. - `FromSeq` — creates a query from any standard `iter.Seq[T]` iterator, including custom collections that expose an iterator method. - `Range`, `Repeat` — generate sequences. The runtime-reflection based `From(any)` constructor from v4 has been removed: in a fully-typed API the element type must be known at the call site. ## Performance v5 eliminates the three taxes the type-erased v4 API paid on every element: interface boxing, type assertions, and reflection. Per-element work in a v5 chain is just typed closure calls; the only allocations are the fixed closure captures made when the query is constructed. Measured on Apple M5 Pro with go1.27, 1M-element `[]int` (100k structs for the projection case): | Benchmark | v4 (`any` API) | v4 (`…T` reflection API) | **v5** | hand-written loop | |---|---|---|---|---| | `Where` → `Sum` | 23.0 ms / 999,754 allocs | 103.9 ms / 3.0M allocs | **1.5 ms / 3 allocs** | 0.45 ms / 0 allocs | | `Where` → `ToSlice` | 16.4 ms / 1.5M allocs | — | **2.5 ms / 38 allocs** | (result slice only) | | struct `Where` → `Select` → `ToSlice` (100k) | 3.33 ms / 226,685 allocs | 18.0 ms / 490k allocs | **0.71 ms / 30 allocs** | 26 allocs | In short: **5–15× faster** than idiomatic v4, **25–77× faster** than the v4 `…T` reflection API, with allocations dropping from *O(n)* to *O(1)* per query. The residual gap to a hand-written loop is the per-element closure call, inherent to any lazy iterator. ## Migrating from v4 See [MIGRATION.md](MIGRATION.md) for a complete v4 → v5 symbol table. The highlights: * `Query` is now `Query[T]`; all operators take typed functions (`func(T) bool` instead of `func(any) bool`). * All `…T` reflection twins (`WhereT`, `SelectT`, …) are gone — the base methods are now just as clean and much faster. * Element-returning terminals (`First`, `Last`, `Single`, `Aggregate`, `Min`, `Max`, …) return `(T, bool)` instead of a nil-able `any`. * `Min`, `Max`, `Sum`, `Average`, `ToMap` are package-level functions (their constraints depend on the element type); chainable `MinBy`, `MaxBy`, `SumBy`, `AverageBy`, `ToMapBy` methods are available. * `ToSlice()` returns `[]T` instead of filling a pointer argument. ## Release Notes ``` … ```

Issues· 4 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Gogeneric-functionsgolinq

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言