A modern Go utility library which provides helpers (map, find, contains, filter, ...)
Go Makefile
Latest commit 81eeb76 Jan 18, 2017 @thoas committed on GitHub Merge pull request #25 from zanven42/patch-1
Readme spelling and wording fix

README.rst

go-funk

Build Status GoDoc Go report

go-funk is a modern Go library based on reflect.

Generic helpers rely on reflect, be careful this code runs exclusively on runtime so you must have a good test suite.

These helpers have started as an experiment to learn reflect. It may looks like lodash in some aspects but it will have its own roadmap, lodash is an awesome library with a lot of works behind it, all features included in go-funk come from internal use cases.

You can also find typesafe implementation in the godoc.

Why this name?

Long story, short answer because func is a reserved word in Go, I wanted something similar.

Initially this project was named fn I don't need to explain why that was a bad idea for french speakers :)

Let's funk!

https://media.giphy.com/media/3oEjHQKtDXpeGN9rW0/giphy.gif

<3

Installation

go get github.com/thoas/go-funk

Usage

import "github.com/thoas/go-funk"

These examples will be based on the following data model:

type Foo struct {
    ID        int
    FirstName string `tag_name:"tag 1"`
    LastName  string `tag_name:"tag 2"`
    Age       int    `tag_name:"tag 3"`
}

func (f Foo) TableName() string {
    return "foo"
}

With fixtures:

f := &Foo{
    ID:        1,
    FirstName: "Foo",
    LastName:  "Bar",
    Age:       30,
}

You can import go-funk using a basic statement:

import "github.com/thoas/go-funk"

funk.Contains

Returns true if an element is present in a iteratee (slice, map, string).

One frustrating thing in Go is to implement contains methods for each types, for example:

func ContainsInt(s []int, e int) bool {
    for _, a := range s {
        if a == e {
            return true
        }
    }
    return false
}

this can be replaced by funk.Contains:

// slice of string
funk.Contains([]string{"foo", "bar"}, "bar") // true

// slice of Foo ptr
funk.Contains([]*Foo{f}, f) // true
funk.Contains([]*Foo{f}, nil) // false

b := &Foo{
    ID:        2,
    FirstName: "Florent",
    LastName:  "Messa",
    Age:       28,
}

funk.Contains([]*Foo{f}, b) // false

// string
funk.Contains("florent", "rent") // true
funk.Contains("florent", "foo") // false

// even map
funk.Contains(map[int]string{1: "Florent"}, 1) // true

funk.IndexOf

Gets the index at which the first occurrence of value is found in array or return -1 if the value cannot be found.

// slice of string
funk.IndexOf([]string{"foo", "bar"}, "bar") // 1
funk.IndexOf([]string{"foo", "bar"}, "gilles") // -1

funk.LastIndexOf

Gets the index at which the last occurrence of value is found in array or return -1 if the value cannot be found.

// slice of string
funk.LastIndexOf([]string{"foo", "bar", "bar"}, "bar") // 2
funk.LastIndexOf([]string{"foo", "bar"}, "gilles") // -1

funk.ToMap

Transforms a slice of structs to a map based on a pivot field.

f := &Foo{
    ID:        1,
    FirstName: "Gilles",
    LastName:  "Fabio",
    Age:       70,
}

b := &Foo{
    ID:        2,
    FirstName: "Florent",
    LastName:  "Messa",
    Age:       80,
}

results := []*Foo{f, b}

mapping := funk.ToMap(results, "ID") // map[int]*Foo{1: f, 2: b}

funk.Filter

Filters a slice based on a predicate.

r := funk.Filter([]int{1, 2, 3, 4}, func(x int) bool {
    return x%2 == 0
}) // []int{2, 4}

funk.Find

Finds an element in a slice based on a predicate.

r := funk.Find([]int{1, 2, 3, 4}, func(x int) bool {
    return x%2 == 0
}) // 2

funk.Map

Manipulates an iteratee (map, slice) and transforms it to another type:

  • map -> slice
  • map -> map
  • slice -> map
  • slice -> slice
r := funk.Map([]int{1, 2, 3, 4}, func(x int) int {
    return x * 2
}) // []int{2, 4, 6, 8}

r := funk.Map([]int{1, 2, 3, 4}, func(x int) string {
    return "Hello"
}) // []string{"Hello", "Hello", "Hello", "Hello"}

r = funk.Map([]int{1, 2, 3, 4}, func(x int) (int, int) {
    return x, x
}) // map[int]int{1: 1, 2: 2, 3: 3, 4: 4}

mapping := map[int]string{
    1: "Florent",
    2: "Gilles",
}

r = funk.Map(mapping, func(k int, v string) int {
    return k
}) // []int{1, 2}

r = funk.Map(mapping, func(k int, v string) (string, string) {
    return fmt.Sprintf("%d", k), v
}) // map[string]string{"1": "Florent", "2": "Gilles"}

funk.Get

Retrieves the value at path of struct(s).

var bar *Bar = &Bar{
    Name: "Test",
    Bars: []*Bar{
        &Bar{
            Name: "Level1-1",
            Bar: &Bar{
                Name: "Level2-1",
            },
        },
        &Bar{
            Name: "Level1-2",
            Bar: &Bar{
                Name: "Level2-2",
            },
        },
    },
}

var foo *Foo = &Foo{
    ID:        1,
    FirstName: "Dark",
    LastName:  "Vador",
    Age:       30,
    Bar:       bar,
    Bars: []*Bar{
        bar,
        bar,
    },
}

funk.Get([]*Foo{foo}, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"}
funk.Get(foo, "Bar.Bars.Bar.Name") // []string{"Level2-1", "Level2-2"}
funk.Get(foo, "Bar.Name") // Test

funk.Get also handles nil values:

bar := &Bar{
    Name: "Test",
}

foo1 := &Foo{
    ID:        1,
    FirstName: "Dark",
    LastName:  "Vador",
    Age:       30,
    Bar:       bar,
}

foo2 := &Foo{
    ID:        1,
    FirstName: "Dark",
    LastName:  "Vador",
    Age:       30,
} // foo2.Bar is nil

funk.Get([]*Foo{foo1, foo2}, "Bar.Name") // []string{"Test"}
funk.Get(foo2, "Bar.Name") // nil

funk.Keys

Creates an array of the own enumerable map keys or struct field names.

funk.Keys(map[string]int{"one": 1, "two": 2}) // []string{"one", "two"} (iteration order is not guaranteed)

foo := &Foo{
    ID:        1,
    FirstName: "Dark",
    LastName:  "Vador",
    Age:       30,
}

funk.Keys(foo) // []string{"ID", "FirstName", "LastName", "Age"} (iteration order is not guaranteed)

funk.Values

Creates an array of the own enumerable map values or struct field values.

funk.Values(map[string]int{"one": 1, "two": 2}) // []string{1, 2} (iteration order is not guaranteed)

foo := &Foo{
    ID:        1,
    FirstName: "Dark",
    LastName:  "Vador",
    Age:       30,
}

funk.Values(foo) // []interface{}{1, "Dark", "Vador", 30} (iteration order is not guaranteed)

funk.ForEach

Range over an iteratee (map, slice).

funk.ForEach([]int{1, 2, 3, 4}, func(x int) {
    fmt.Println(x)
})

funk.ForEachRight

Range over an iteratee (map, slice) from the right.

results := []int{}

funk.ForEachRight([]int{1, 2, 3, 4}, func(x int) {
    results = append(results, x)
})

fmt.Println(results) // []int{4, 3, 2, 1}

funk.Chunk

Creates an array of elements split into groups with the length of the size. If array can't be split evenly, the final chunk will be the remaining element.

funk.Chunk([]int{1, 2, 3, 4, 5}, 2) // [][]int{[]int{1, 2}, []int{3, 4}, []int{5}}

funk.FlattenDeep

Recursively flattens array.

funk.FlattenDeep([][]int{[]int{1, 2}, []int{3, 4}}) // []int{1, 2, 3, 4}

funk.Uniq

Creates an array with unique values.

funk.Uniq([]int{0, 1, 1, 2, 3, 0, 0, 12}) // []int{0, 1, 2, 3, 12}

funk.Initial

Gets all but the last element of array.

funk.Initial([]int{0, 1, 2, 3, 4}) // []int{0, 1, 2, 3}

funk.Tail

Gets all but the first element of array.

funk.Tail([]int{0, 1, 2, 3, 4}) // []int{1, 2, 3, 4}

funk.Shuffle

Creates an array of shuffled values.

funk.Shuffle([]int{0, 1, 2, 3, 4}) // []int{2, 1, 3, 4, 0}

funk.Sum

Computes the sum of the values in array.

funk.Sum([]int{0, 1, 2, 3, 4}) // 10.0
funk.Sum([]interface{}{0.5, 1, 2, 3, 4}) // 10.5

funk.Reverse

Transforms an array the first element will become the last, the second element will become the second to last, etc.

funk.Reverse([]int{0, 1, 2, 3, 4}) // []int{4, 3, 2, 1, 0}

funk.SliceOf

Returns a slice based on an element.

funk.SliceOf(f) // will return a []*Foo{f}

funk.RandomInt

Generates a random int, based on a min and max values.

funk.RandomInt(0, 100) // will be between 0 and 100

funk.RandomString

Generates a random string with a fixed length.

funk.RandomString(4) // will be a string of 4 random characters

funk.Shard

Generates a sharded string with a fixed length and depth.

funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 1, 2, false) // []string{"e", "8", "e89d66bdfdd4dd26b682cc77e23a86eb"}

funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 2, false) // []string{"e8", "9d", "e89d66bdfdd4dd26b682cc77e23a86eb"}

funk.Shard("e89d66bdfdd4dd26b682cc77e23a86eb", 2, 2, true) // []string{"e8", "9d", "66", "bdfdd4dd26b682cc77e23a86eb"}

Performance

go-funk has currently an open issue about performance, don't hesitate to participate in the discussion to enhance the generic helpers implementations.

Let's stop beating around the bush, a typesafe implementation in pure Go of funk.Contains, let's say for example:

func ContainsInt(s []int, e int) bool {
    for _, a := range s {
        if a == e {
            return true
        }
    }
    return false
}

will always outperform an implementation based on reflect in terms of speed and allocs because of how it's implemented in the language.

If you want a similarity gorm will always be slower than sqlx (which is very low level btw) and will uses more allocs.

You must not think generic helpers of go-funk as a replacement when you are dealing with performance in your codebase, you should use typesafe implementations instead.

Contributing

Don't hesitate ;)