Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

damoang/gg

<<<<<<< HEAD

gg

=======

gg: Go Generics

GoDoc Go Report Card Go Coverage License

English | 简体中文

🔥bytedance/gg is a basic library of generics for Go language developed by ByteDance. It is based on the Go 1.18+ generic features and provides efficient, type-safe and rich generic data structures and tool functions.

Why this name?

Take the first letter of Go Generics, short and simple.

Why choose gg?

  • Stable and reliable: It is a necessary tool library for ByteDance R&D team, and it has 1w+ repository references inside.
  • Easy to use: With the design principle of simplicity and self-consistent, subcontracted according to functions, modular, semantic intuitive and unified, and low learning cost.
  • High Performance: Provides high-performance concurrent data structures, with performance 10+ times faster than standard library.
  • No three-party dependencies: Generic libraries will not introduce any three-party dependencies.
  • Version control: Follow the SemVer, guaranteeing backward compatibility.

🚀 Install

go get github.com/bytedance/gg

🔎 Table of contents

✨ Generic Functional Programming

goption

Option type, simplifying the processing of (T, bool)

Usage:

import (
 "github.com/bytedance/gg/goption"
)

Example:

goption.Of(1, true).Value()
// 1
goption.Nil[int]().IsNil()
// true
goption.Nil[int]().ValueOr(10)
// 10
goption.OK(1).IsOK()
// true
goption.OK(1).ValueOrZero()
// 1
goption.OfPtr((*int)(nil)).Ptr()
// nil
goption.Map(goption.OK(1), strconv.Itoa).Get()
// "1" true

gresult

Result type, simplifying the processing of (T, error)

Usage:

import (
 "github.com/bytedance/gg/gresult"
)

Example:

gresult.Of(strconv.Atoi("1")).Value()
// 1
gresult.Err[int](io.EOF).IsErr()
// true
gresult.Err[int](io.EOF).ValueOr(10)
// 10
gresult.OK(1).IsOK()
// true
gresult.OK(1).ValueOrZero()
// 1
gresult.Of(strconv.Atoi("x")).Option().Get()
// 0 false
gresult.Map(gresult.OK(1), strconv.Itoa).Get()
// "1" nil

✨ Generic Data Processing

gcond:

Conditional operation

Usage:

import (
 "github.com/bytedance/gg/gcond"
)

Example:

gcond.If(true, 1, 2)
// 1
var a *struct{ A int }
getA := func() int { return a.A }
get1 := func() int { return 1 }
gcond.IfLazy(a != nil, getA, get1)
// 1
gcond.IfLazyL(a != nil, getA, 1)
// 1
gcond.IfLazyR(a == nil, 1, getA)
// 1
gcond.Switch[string](3).
 Case(1, "1").
 CaseLazy(2, func() string { return "3" }).
 When(3, 4).Then("3/4").
 When(5, 6).ThenLazy(func() string { return "5/6" }).
 Default("other")
// 3/4

gvalue

Processing value T

Usage:

import (
 "github.com/bytedance/gg/gvalue"
)

Example1:Zero Value

a := gvalue.Zero[int]()
// 0
gvalue.IsZero(a)
// true
b := gvalue.Zero[*int]()
// nil
gvalue.IsNil(b)
// true
gvalue.Or(0, 1, 2)
// 1

Example2:Math Operation

gvalue.Max(1, 2, 3)
// 3
gvalue.Min(1, 2, 3)
// 1
gvalue.MinMax(1, 2, 3)
// 1 3
gvalue.Clamp(5, 1, 10)
// 5
gvalue.Add(1, 2)
// 3

Example3:Comparison

gvalue.Equal(1, 1)
// true
gvalue.Between(2, 1, 3)
// true

Example4:Type Assertion

gvalue.TypeAssert[int](any(1))
// 1
gvalue.TryAssert[int](any(1))
// 1 true

gptr

Processing pointer *T

Usage:

import (
 "github.com/bytedance/gg/gptr"
)

Example:

a := Of(1)
gptr.Indirect(a)
// 1
b := OfNotZero(1)
gptr.IsNotNil(b)
// true
gptr.IndirectOr(b, 2)
// 1
gptr.Indirect(gptr.Map(b, strconv.Itoa))
// "1"
c := OfNotZero(0)
// nil
gptr.IsNil(c)
// true
gptr.IndirectOr(c, 2)
// 2

gslice

Processing slice []T

Usage:

import (
 "github.com/bytedance/gg/gslice"
)

Example1:High-order Function

gslice.Map([]int{1, 2, 3, 4, 5}, strconv.Itoa)
// ["1", "2", "3", "4", "5"]
isEven := func(i int) bool { return i%2 == 0 }
gslice.Filter([]int{1, 2, 3, 4, 5}, isEven)
// [2, 4]
gslice.Reduce([]int{1, 2, 3, 4, 5}, gvalue.Add[int].Value())
// 15
gslice.Any([]int{1, 2, 3, 4, 5}, isEven)
// true
gslice.All([]int{1, 2, 3, 4, 5}, isEven)
// false

Example2:CURD Operation

gslice.Contains([]int{1, 2, 3, 4, 5}, 2)
// true
gslice.ContainsAny([]int{1, 2, 3, 4, 5}, 2, 6)
// true
gslice.ContainsAll([]int{1, 2, 3, 4, 5}, 2, 6)
// false
gslice.Index([]int{1, 2, 3, 4, 5}, 3.Value())
// 2
gslice.Find([]int{1, 2, 3, 4, 5}, isEven).Value()
// 2
gslice.First([]int{1, 2, 3, 4, 5}).Value()
// 1
gslice.Get([]int{1, 2, 3, 4, 5}, 1).Value()
// 2
gslice.Get([]int{1, 2, 3, 4, 5}, -1).Value() // Access element with negative index
// 5

Example3:Partion Operation

gslice.Range(1, 5)
// [1, 2, 3, 4]
gslice.RangeWithStep(5, 1, -2)
// [5, 3]
gslice.Take([]int{1, 2, 3, 4, 5}, 2)
// [1, 2]
gslice.Take([]int{1, 2, 3, 4, 5}, -2)
// [4, 5]
gslice.Slice([]int{1, 2, 3, 4, 5}, 1, 3)
// [2, 3]
gslice.Chunk([]int{1, 2, 3, 4, 5}, 2)
// [[1, 2], [3, 4], [5]]
gslice.Divide([]int{1, 2, 3, 4, 5}, 2)
// [[1, 2, 3], [4, 5]]
gslice.Concat([]int{1, 2}, []int{3, 4, 5})
// [1, 2, 3, 4, 5]
gslice.Flatten([][]int{{1, 2}, {3, 4, 5}})
// [1, 2, 3, 4, 5]
gslice.Partition([]int{1, 2, 3, 4, 5}, isEven)
// [2, 4], [1, 3, 5]

Example4:Math Operation

gslice.Max([]int{1, 2, 3, 4, 5}).Value()
// 5
gslice.Min([]int{1, 2, 3, 4, 5}).Value()
// 1
gslice.MinMax([]int{1, 2, 3, 4, 5}).Value().Values()
// 1 5
gslice.Sum([]int{1, 2, 3, 4, 5})
// 15

Example5:Convert to map

ToMap([]int{1, 2, 3, 4, 5}, func(i int) (string, int) { return strconv.Itoa(i), i })
// {"1":1, "2":2, "3":3, "4":4, "5":5}
ToMapValues([]int{1, 2, 3, 4, 5}, strconv.Itoa)
// {"1":1, "2":2, "3":3, "4":4, "5":5}
GroupBy([]int{1, 2, 3, 4, 5}, func(i int) string {
 if i%2 == 0 {
 return "even"
 } else {
 return "odd"
 }
})
// {"even":[2,4], "odd":[1,3,5]}

Example6:Set Operation

gslice.Union([]int{1, 2, 3}, []int{3, 4, 5})
// [1, 2, 3, 4, 5]
gslice.Intersect([]int{1, 2, 3}, []int{3, 4, 5})
// [3]
gslice.Diff([]int{1, 2, 3}, []int{3, 4, 5})
// [1, 2]
gslice.Uniq([]int{1, 1, 2, 2, 3})
// [1, 2, 3]
gslice.Dup([]int{1, 1, 2, 2, 3})
// [1, 2]

Example7:Re-order Operation

s1 := []int{5, 1, 2, 3, 4}
s2, s3, s4 := Clone(s1), Clone(s1), Clone(s1)
Sort(s1)
// [1, 2, 3, 4, 5]
SortBy(s2, func(i, j int) bool { return i > j })
// [5, 4, 3, 2, 1]
StableSortBy(s3, func(i, j int) bool { return i > j })
// [5, 4, 3, 2, 1]
Reverse(s4)
// [4, 3, 2, 1, 5]

gmap

Processing map map[K]V

Usage:

import (
 "github.com/bytedance/gg/gmap"
)

Example1:Keys / Values Getter

gmap.Keys(map[int]int{1: 2})
// [1]
gmap.Values(map[int]int{1: 2})
// [2]
gmap.Items(map[int]int{1: 2}).Unzip()
// [1] [2]
gmap.OrderedKeys(map[int]int{1: 2, 2: 3, 3: 4})
// [1, 2, 3]
gmap.OrderedValues(map[int]int{1: 2, 2: 3, 3: 4})
// [2, 3, 4]
gmap.OrderedItems(map[int]int{1: 2, 2: 3, 3: 4}).Unzip()
// [1, 2, 3] [2, 3, 4]
f := func(k, v int) string { return strconv.Itoa(k) + ":" + strconv.Itoa(v) }
gmap.ToSlice(map[int]int{1: 2}, f)
// ["1:2"]
gmap.ToOrderedSlice(map[int]int{1: 2, 2: 3, 3: 4}, f)
// ["1:2", "2:3", "3:4"]

Example2:High-order Function

gmap.Map(map[int]int{1: 2, 2: 3, 3: 4}, func(k int, v int) (string, string) {
 return strconv.Itoa(k), strconv.Itoa(k + 1)
})
// {"1":"2", "2":"3", "3":"4"}
gmap.Filter(map[int]int{1: 2, 2: 3, 3: 4}, func(k int, v int) bool {
 return k+v > 3
})
// {"2":2, "3":3}

Example3:CURD Operation

gmap.Contains(map[int]int{1: 2, 2: 3, 3: 4}, 1)
// true
gmap.ContainsAny(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4)
// true
gmap.ContainsAll(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4)
// false
gmap.Load(map[int]int{1: 2, 2: 3, 3: 4}, 1).Value()
// 2
gmap.LoadAny(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4).Value()
// 2
gmap.LoadAll(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4)
// []
gmap.LoadSome(map[int]int{1: 2, 2: 3, 3: 4}, 1, 4)
// [2]

Example4:Partion Operation

Chunk(map[int]int{1: 2, 2: 3, 3: 4, 4: 5, 5: 6}, 2)
// possible result: [{1:2, 2:3}, {3:4, 4:5}, {5:6}]
Divide(map[int]int{1: 2, 2: 3, 3: 4, 4: 5, 5: 6}, 2)
// possible result: [{1:2, 2:3, 3:4}, {4:5, 5:6}]

Example5:Math Operation

gmap.Max(map[int]int{1: 2, 2: 3, 3: 4}).Value()
// 4
gmap.Min(map[int]int{1: 2, 2: 3, 3: 4}).Value()
// 2
gmap.MinMax(map[int]int{1: 2, 2: 3, 3: 4}).Value().Values()
// 2 4
gmap.Sum(map[int]int{1: 2, 2: 3, 3: 4})
// 9

Example6:Set Operation

gmap.Union(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16})
// {1:2, 2:3, 3:14, 4:15, 5:16}
gmap.Intersect(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16})
// {3:14}
gmap.Diff(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16})
// {1:2, 2:3}
gmap.UnionBy(gslice.Of(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16}), DiscardNew[int, int]())
// {1:2, 2:3, 3:4, 4:15, 5:16}
gmap.IntersectBy(gslice.Of(map[int]int{1: 2, 2: 3, 3: 4}, map[int]int{3: 14, 4: 15, 5: 16}), DiscardNew[int, int]())
// {3:4}

gfunc

Processing function func

Usage:

import (
 "github.com/bytedance/gg/gfunc"
)

Example1:Partial Application

add := Partial2(gvalue.Add[int]) // convert the Add function into a partial function
add1 := add.Partial(1) // Bind (i.e., "freeze") the first argument to 1
add1(0) // 0 + 1 = 1
// 1
add1(1) // Reuse the partially applied function: 1 + 1 = 2
// 2
add1n2 := add1.PartialR(2) // Bind the remaining (rightmost) argument to 2; all arguments are now fixed
add1n2() // 1 + 2 = 3

gconv

Data type conversion

Usage:

import (
 "github.com/bytedance/gg/gconv"
)

Example:

gconv.To[string](1)
// "1"
gconv.To[int]("1")
// 1
gconv.To[int]("x")
// 0
gconv.To[bool]("true")
// true
gconv.To[bool]("x")
// false
gconv.To[int](gptr.Of(gptr.Of(gptr.Of("1"))))
// 1
type myInt int
type myString string
gconv.To[myInt](myString("1"))
// 1
gconv.To[myString](myInt(1))
// "1"
gconv.ToE[int]("x")
// 0 strconv.ParseInt: parsing "x": invalid syntax

gson

Processing JSON

Usage:

import (
 "github.com/bytedance/gg/gson"
)

Example:

type testStruct struct {
 Name string `json:"name"`
 Age int `json:"age"`
}
testcase := testStruct{Name: "test", Age: 10}
gson.Marshal(testcase)
// []byte(`{"name":"test","age":10}`) nil
gson.MarshalString(testcase)
// `{"name":"test","age":10}` nil
gson.ToString(testcase)
// `{"name":"test","age":10}`
gson.MarshalIndent(testcase, "", " ")
// "{\n \"name\": \"test\",\n \"age\": 10\n}" nil
gson.ToStringIndent(testcase, "", " ")
// "{\n \"name\": \"test\",\n \"age\": 10\n}"
gson.Valid(`{"name":"test","age":10}`)
// true
gson.Unmarshal[testStruct](`{"name":"test","age":10}`)
// {test 10} nil
// Use high-performance JSON codecs such as Sonic or json-iterator, instead of the standard library's encoding/json.
import "github.com/bytedance/sonic"
gson.MarshalBy(sonic.ConfigDefault, testcase)
// []byte(`{"name":"test","age":10}`) nil
gson.MarshalString(sonic.ConfigDefault, testcase)
// {"name":"test","age":10}`, nil
gson.UnmarshalBy[testStruct](sonic.ConfigDefault, `{"name":"test","age":10}`)
// testStruct{Name: "test", Age: 10}, nil
// Example using Json-Iterator:
import jsoniter "github.com/json-iterator/go"
gson.MarshalBy(jsoniter.ConfigDefault, testcase)
// []byte(`{"name":"test","age":10}`) nil
gson.MarshalString(jsoniter.ConfigDefault, testcase) 
// {"name":"test","age":10}`, nil
gson.UnmarshalBy[testStruct](jsoniter.ConfigDefault, `{"name":"test","age":10}`)
// testStruct{Name: "test", Age: 10}, nil

✨ Generic Standard Wrapper

gsync

Wrap sync

Usage:

import (
 "github.com/bytedance/gg/gstd/gsync"
)

Example1:gsync.Map wraps sync.Map

sm := gsync.Map[string, int]{}
sm.Store("k", 1)
sm.Load("k")
// 1 true
sm.LoadO("k").Value()
// 1
sm.Store("k", 2)
sm.Load("k")
// 2 true
sm.LoadAndDelete("k")
// 2 true
sm.Load("k")
// 0 false
sm.LoadOrStore("k", 3)
// 3 false
sm.Load("k")
// 3 true
sm.ToMap()
// {"k":3}

Example2:gsync.Pool wraps sync.Pool

pool := Pool[*int]{
 New: func() *int {
 i := 1
 return &i
 },
}
a := pool.Get()
*a
// 1
*a = 2
pool.Put(a)
*pool.Get()
// possible result: 1 or 2

Example3:gsync.OnceXXX wraps sync.Once

onceFunc := gsync.OnceFunc(func() { fmt.Println("OnceFunc") })
onceFunc()
// "OnceFunc"
onceFunc()
// (no output)
onceFunc()
// (no output)
i := 1
onceValue := gsync.OnceValue(func() int { i++; return i })
onceValue()
// 2
onceValue()
// 2
onceValues := gsync.OnceValues(func() (int, error) { i++; return i, nil })
onceValues()
// 3 nil
onceValues()
// 3 nil

✨ Generic Data Structures

tuple

Implementation of tuple provides definition of generic n-ary tuples

Usage

import (
 "github.com/bytedance/gg/collection/tuple"
)

Example:

addr := Make2("localhost", 8080)
fmt.Printf("%s:%d\n", addr.First, addr.Second)
// localhost:8080
s := Zip2([]string{"red", "green", "blue"}, []int{14, 15, 16})
for _, v := range s {
 fmt.Printf("%s:%d\n", v.First, v.Second)
}
// red:14
// green:15
// blue:16
s.Unzip()
// ["red", "green", "blue"] [14, 15, 16]

set

Implementation of set based on map[T]struct{}

Usage

import (
 "github.com/bytedance/gg/collection/set"
)

Example:

s := New(10, 10, 12, 15)
s.Len()
// 3
s.Add(10)
// false
s.Add(11)
// true
s.Remove(11) && s.Remove(12)
// true
s.ContainsAny(10, 15)
// true
s.ContainsAny(11, 12)
// false
s.ContainsAny()
// false
s.ContainsAll(10, 15)
// true
s.ContainsAll(10, 11)
// false
s.ContainsAll()
// true
len(s.ToSlice())
// 2

list

Implementation of doubly linked list

Usage

import (
 "github.com/bytedance/gg/collection/list"
)

Example:

l := New[int]()
e1 := l.PushFront(1) // 1
e2 := l.PushBack(2) // 1->2
e3 := l.InsertBefore(3, e2) // 1->3->2
e4 := l.InsertAfter(4, e1) // 1->4->3->2
l.MoveToFront(e4) // 4->1->3->2
l.MoveToBack(e1) // 4->3->2->1
l.MoveAfter(e3, e2) // 4->2->3->1
l.MoveBefore(e4, e1) // 2->3->4->1
l.Len()
// 4
l.Front().Value
// 2
l.Back().Value
// 1
for e := l.Front(); e != nil; e = e.Next() {
 fmt.Println(e.Value) // 2 3 4 1
}

skipset

High-performance, scalable, concurrent-safe set based on skip-list, up to 15x faster than the built-in sync.Map below Go 1.24

⚠️ NOTICE: Go 1.24 or later, please consider using the std sync.Map, which has better performance compared to skipset in about 90% of use cases.

Usage

import (
 "github.com/bytedance/gg/collection/skipset"
)

Example:

s := skipset.New[int]()
s.Add(10)
// true
s.Add(10)
// false
s.Add(11)
// true
s.Add(12)
// true
s.Len()
// 3
s.Contains(10)
// true
s.Remove(10)
// true
s.Contains(10)
// false
s.ToSlice()
// [11, 12]
var wg sync.WaitGroup
wg.Add(1000)
for i := 0; i < 1000; i++ {
 i := i
 go func() {
 defer wg.Done()
 s.Add(i)
 }()
}
wg.Wait()
s.Len()
// 1000

skipmap

High-performance, scalable, concurrent-safe map based on skip-list, up to 10x faster than the built-in sync.Map below Go 1.24

⚠️ Go 1.24 or later, please consider using the std sync.Map, which has better performance compared to skipmap in about 90% of use cases.

Usage

import (
 "github.com/bytedance/gg/collection/skipmap"
)

Example:

s := New[string, int]()
s.Store("a", 0)
s.Store("a", 1)
s.Store("b", 2)
s.Store("c", 3)
s.Len()
// 3
s.Load("a")
// 1 true
s.LoadAndDelete("a")
// 1 true
s.LoadOrStore("a", 11)
// 11 false
gson.ToString(s.ToMap())
// {"a":11, "b":2, "c": 3}
s.Delete("a")
s.Delete("b")
s.Delete("c")
var wg sync.WaitGroup
wg.Add(1000)
for i := 0; i < 1000; i++ {
 i := i
 go func() {
 defer wg.Done()
 s.Store(strconv.Itoa(i), i)
 }()
}
wg.Wait()
s.Len()
// 1000

License

gg is licensed under the Apache-2.0 license. See LICENSE for details.

2025 © Bytedance

upstream/main

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /