- Notifications
You must be signed in to change notification settings - Fork225
.NET LINQ capabilities in Go
License
ahmetb/go-linq
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
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
When used with Go modules, use the following import path:
go get github.com/ahmetb/go-linq/v3
Older versions of Go using different dependency management tools can use thefollowing import path to prevent breaking API changes:
go get gopkg.in/ahmetb/go-linq.v3
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
import ."github.com/ahmetb/go-linq/v3"typeCarstruct {yearintowner,modelstring}...varowners []stringFrom(cars).Where(func(cinterface{})bool {returnc.(Car).year>=2015}).Select(func(cinterface{})interface{} {returnc.(Car).owner}).ToSlice(&owners)
Or, you can use generic functions, likeWhereT
andSelectT
to simplify your code(at a performance penalty):
varowners []stringFrom(cars).WhereT(func(cCar)bool {returnc.year>=2015}).SelectT(func(cCar)string {returnc.owner}).ToSlice(&owners)
Example 2: Find the author who has written the most books
import ."github.com/ahmetb/go-linq/v3"typeBookstruct {idinttitlestringauthors []string}author:=From(books).SelectMany(// make a flat array of authorsfunc(bookinterface{})Query {returnFrom(book.(Book).authors)}).GroupBy(// group by authorfunc(authorinterface{})interface{} {returnauthor// author as key},func(authorinterface{})interface{} {returnauthor// author as value}).OrderByDescending(// sort groups by its lengthfunc(groupinterface{})interface{} {returnlen(group.(Group).Group)}).Select(// get authors out of groupsfunc(groupinterface{})interface{} {returngroup.(Group).Key}).First()// take the first author
Example 3: Implement a custom method that leaves only values greater than the specified threshold
typeMyQueryQueryfunc (qMyQuery)GreaterThan(thresholdint)Query {returnQuery{Iterate:func()Iterator {next:=q.Iterate()returnfunc() (iteminterface{},okbool) {foritem,ok=next();ok;item,ok=next() {ifitem.(int)>threshold {return}}return}},}}result:=MyQuery(Range(1,10)).GreaterThan(5).Results()
Although Go doesn't implement generics, with some reflection tricks, you can use go-linq withouttypinginterface{}
s and type assertions. This will introduce a performance penalty (5x-10x slower)but will yield in a cleaner and more readable code.
Methods withT
suffix (such asWhereT
) accept functions with generic types. So instead of
.Select(func(v interface{}) interface{} {...})
you can type:
.SelectT(func(v YourType) YourOtherType {...})
This will make your code free ofinterface{}
and type assertions.
Example 4: "MapReduce" in a slice of string sentences to list the top 5 most used words using generic functions
varresults []stringFrom(sentences).// split sentences to wordsSelectManyT(func(sentencestring)Query {returnFrom(strings.Split(sentence," "))}).// group the wordsGroupByT(func(wordstring)string {returnword },func(wordstring)string {returnword },).// order by countOrderByDescendingT(func(wordGroupGroup)int {returnlen(wordGroup.Group)}).// order by the wordThenByT(func(wordGroupGroup)string {returnwordGroup.Key.(string)}).Take(5).// take the top 5// project the words using the index as rankSelectIndexedT(func(indexint,wordGroupGroup)string {returnfmt.Sprintf("Rank: #%d, Word: %s, Counts: %d",index+1,wordGroup.Key,len(wordGroup.Group))}).ToSlice(&results)
More examples can be found in thedocumentation.
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 comparablesv0.9-rc4* GroupBy()v0.9-rc3.2* bugfix: All() iterating over values instead of indicesv0.9-rc3.1* bugfix: modifying result slice affects subsequent query methodsv0.9-rc3* removed FirstOrNil, LastOrNil, ElementAtOrNil methodsv0.9-rc2.5* slice-accepting methods accept slices of any type with reflectionsv0.9-rc2* parallel linq (plinq) implemented* Queryable separated into Query & ParallelQuery* fixed early termination for Allv0.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
About
.NET LINQ capabilities in Go