An open API service indexing awesome lists of open source software.

https://github.com/ahmetb/go-linq

.NET LINQ capabilities in Go
https://github.com/ahmetb/go-linq

generic-functions go linq

Last synced: 4 months ago
JSON representation

.NET LINQ capabilities in Go

Awesome Lists containing this project

README

          

# go-linq [![GoDoc](https://godoc.org/github.com/ahmetb/go-linq?status.svg)](https://godoc.org/github.com/ahmetb/go-linq) [![Build Status](https://github.com/ahmetb/go-linq/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/ahmetb/go-linq/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/github/ahmetb/go-linq/badge.svg?branch=master)](https://coveralls.io/github/ahmetb/go-linq?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/ahmetb/go-linq)](https://goreportcard.com/report/github.com/ahmetb/go-linq)

A powerful language integrated query (LINQ) library for Go.

* Written in vanilla Go, no dependencies!
* Complete lazy evaluation with iterator pattern
* Safe for concurrent use
* Supports generic functions to make your code cleaner and free of type assertions
* Supports arrays, slices, maps, strings, channels and custom collections

## Installation

When used with Go modules, use the following import path:

go get github.com/ahmetb/go-linq/v4

Older versions of Go using different dependency management tools can use the
following import path to prevent breaking API changes:

go get gopkg.in/ahmetb/go-linq.v4

## Quickstart

Usage is as easy as chaining methods like:

`From(slice)` `.Where(predicate)` `.Select(selector)` `.Union(data)`

**Example 1: Find all owners of cars manufactured after 2015**

```go
import . "github.com/ahmetb/go-linq/v4"

type Car struct {
year int
owner, model string
}

...

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)
```

Or, you can use generic functions, like `WhereT` and `SelectT` to simplify your code
(at a performance penalty):

```go
var owners []string

FromSlice(cars).WhereT(func(c Car) bool {
return c.year >= 2015
}).SelectT(func(c Car) string {
return c.owner
}).ToSlice(&owners)
```

**Example 2: Find the author who has written the most books**

```go
import . "github.com/ahmetb/go-linq/v4"

type Book struct {
id int
title string
authors []string
}

author := FromSlice(books).SelectMany( // make a flat array of authors
func(book any) Query {
return From(book.(Book).authors)
}).GroupBy( // group by author
func(author any) any {
return author // author as key
}, func(author any) any {
return author // author as value
}).OrderByDescending( // sort groups by its length
func(group any) any {
return len(group.(Group).Group)
}).Select( // get authors out of groups
func(group any) any {
return group.(Group).Key
}).First() // take the first author
```

**Example 3: Implement a custom method that leaves only values greater than the specified threshold**

```go
type MyQuery Query

func (q MyQuery) GreaterThan(threshold int) Query {
return Query{
Iterate: func(yield func(any) bool) {
q.Iterate(func(item any) bool {
if item.(int) > threshold {
return yield(item)
}
return true
})
},
}
}

result := MyQuery(Range(1,10)).GreaterThan(5).Results()
```

## Generic Functions

Although Go doesn't implement generics, with some reflection tricks, you can use go-linq without
typing `any`s and type assertions. This will introduce a performance penalty (5x-10x slower)
but will yield in a cleaner and more readable code.

Methods with `T` suffix (such as `WhereT`) accept functions with generic types. So instead of

.Select(func(v any) any {...})

you can type:

.SelectT(func(v YourType) YourOtherType {...})

This will make your code free of `any` and type assertions.

**Example 4: "MapReduce" in a slice of string sentences to list the top 5 most used words using generic functions**

```go
var results []string

FromSlice(sentences).
// split sentences to words
SelectManyT(func(sentence string) Query {
return From(strings.Split(sentence, " "))
}).
// group the words
GroupByT(
func(word string) string { return word },
func(word string) string { return word },
).
// order by count
OrderByDescendingT(func(wordGroup Group) int {
return len(wordGroup.Group)
}).
// order by the word
ThenByT(func(wordGroup Group) string {
return wordGroup.Key.(string)
}).
Take(5). // take the top 5
// project the words using the index as rank
SelectIndexedT(func(index int, wordGroup Group) string {
return fmt.Sprintf("Rank: #%d, Word: %s, Counts: %d", index+1, wordGroup.Key, len(wordGroup.Group))
}).
ToSlice(&results)
```

## Manual Iteration

Since **go-linq v4** manual iteration follows Go’s standard iterator pattern introduced with the `iter` package.
The Query type exposes an `Iterate` field of type `iter.Seq[any]`, making it easier to integrate with Go’s native
iteration style.

**Example 5: 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)
}
```

**More examples** can be found in the [documentation](https://godoc.org/github.com/ahmetb/go-linq).

## Data Source Constructors

Since **go-linq v4**, a new family of constructor functions provides a type-safe and efficient way to create queries from
various data sources. Each function is optimized for its specific input type, avoiding the overhead of reflection.

Available constructors:
- `FromSlice` - creates a query from a slice
- `FromMap` - creates a query from a map
- `FromChannel` - creates a query from a channel
- `FromChannelWithContext` - creates a query from a channel with `Context` support
- `FromString` - creates a query from a string (iterating over runes)
- `FromIterable` - creates a query from a custom collection implementing the `Iterable` interface

The older `From` function remains available for backward compatibility, but it relies on runtime reflection and is
significantly less efficient. For all new code, it’s recommended to use the explicit `From*` constructors.

## Release Notes

```text
v4.0.0 (2025-10-12)
* Breaking change: Migrated to standard Go iterator pattern. (thanks @kalaninja!)
* Added typed constructors: FromSlice(), FromMap(), FromChannel(),
FromChannelWithContext(), FromString().
* Breaking change: Removed FromChannelT() in favor of FromChannel().

v3.2.0 (2020-12-29)
* Added FromChannelT().
* Added DefaultIfEmpty().

v3.1.0 (2019-07-09)
* Support for Go modules
* Added IndexOf()/IndexOfT().

v3.0.0 (2017-01-10)
* Breaking change: ToSlice() now overwrites existing slice starting
from index 0 and grows/reslices it as needed.
* Generic methods support (thanks @cleitonmarx!)
- Accepting parametrized functions was originally proposed in #26
- You can now avoid type assertions and interface{}s
- Functions with generic methods are named as "MethodNameT" and
signature for the existing LINQ methods are unchanged.
* Added ForEach(), ForEachIndexed() and AggregateWithSeedBy().

v2.0.0 (2016-09-02)
* IMPORTANT: This release is a BREAKING CHANGE. The old version
is archived at the 'archive/0.9' branch or the 0.9 tags.
* A COMPLETE REWRITE of go-linq with better performance and memory
efficiency. (thanks @kalaninja!)
* API has significantly changed. Most notably:
- linq.T removed in favor of interface{}
- library methods no longer return errors
- PLINQ removed for now (see channels support)
- support for channels, custom collections and comparables

v0.9-rc4
* GroupBy()

v0.9-rc3.2
* bugfix: All() iterating over values instead of indices

v0.9-rc3.1
* bugfix: modifying result slice affects subsequent query methods

v0.9-rc3
* removed FirstOrNil, LastOrNil, ElementAtOrNil methods

v0.9-rc2.5
* slice-accepting methods accept slices of any type with reflections

v0.9-rc2
* parallel linq (plinq) implemented
* Queryable separated into Query & ParallelQuery
* fixed early termination for All

v0.9-rc1
* many linq methods are implemented
* methods have error handling support
* type assertion limitations are unresolved
* travis-ci.org build integrated
* open sourced on github, master & dev branches
```