Convert Figma logo to code with AI

tobyhede logogo-underscore

Helpfully Functional Go - A useful collection of Go utilities. Designed for programmer happiness.

1,297
61
1,297
4

Top Related Projects

17,335

💥 A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...)

4,744

A modern Go utility library which provides helpers (map, find, contains, filter, ...)

1,940

🍕 Enjoy a slice! A utility library for dealing with slices and maps that focuses on type safety and performance.

4,393

A comprehensive, efficient, and reusable util function library of Go.

Quick Overview

Go-underscore is a utility library for Go that provides functional programming helpers inspired by Underscore.js. It offers a collection of functions to manipulate arrays, maps, and other data structures in a more functional and concise manner, enhancing Go's standard library capabilities.

Pros

  • Brings functional programming concepts to Go, making certain operations more expressive
  • Provides a familiar API for developers coming from JavaScript/Underscore.js background
  • Reduces boilerplate code for common operations on collections and data structures
  • Implements lazy evaluation for improved performance in certain scenarios

Cons

  • Adds an external dependency to projects, which may not align with Go's philosophy of minimal dependencies
  • Some functions may have performance overhead compared to native Go implementations
  • Limited maintenance and updates (last commit was several years ago)
  • May encourage non-idiomatic Go code in some cases

Code Examples

  1. Filtering a slice:
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
evenNumbers := __.Filter(numbers, func(n, _ int) bool {
    return n%2 == 0
})
// evenNumbers: [2, 4, 6, 8, 10]
  1. Mapping over a slice:
names := []string{"John", "Jane", "Bob"}
upperNames := __.Map(names, func(name string, _ int) string {
    return strings.ToUpper(name)
})
// upperNames: ["JOHN", "JANE", "BOB"]
  1. Reducing a slice:
numbers := []int{1, 2, 3, 4, 5}
sum := __.Reduce(numbers, func(memo, num, _ int) int {
    return memo + num
}, 0)
// sum: 15

Getting Started

To use go-underscore in your Go project, follow these steps:

  1. Install the package:

    go get github.com/tobyhede/go-underscore
    
  2. Import the package in your Go file:

    import __ "github.com/tobyhede/go-underscore"
    
  3. Start using the functions:

    result := __.Map([]int{1, 2, 3}, func(n, _ int) int {
        return n * 2
    })
    fmt.Println(result) // Output: [2, 4, 6]
    

Competitor Comparisons

17,335

💥 A Lodash-style Go library based on Go 1.18+ Generics (map, filter, contains, find...)

Pros of lo

  • More actively maintained with recent updates and contributions
  • Broader range of utility functions, including error handling and concurrency helpers
  • Generics support for improved type safety and reduced boilerplate

Cons of lo

  • Larger codebase and API surface, potentially increasing complexity
  • May have a steeper learning curve for newcomers compared to go-underscore
  • Some functions might overlap with standard library capabilities

Code Comparison

lo:

names := lo.Map([]string{"Samuel", "John", "Alice"}, func(name string, _ int) string {
    return strings.ToUpper(name)
})

go-underscore:

names := __.Map([]string{"Samuel", "John", "Alice"}, func(name string, _ int) string {
    return strings.ToUpper(name)
})

Both libraries provide similar functionality for common operations like mapping, but lo offers a more extensive set of utilities and leverages Go's generics for improved type safety. While go-underscore is simpler and may be easier to grasp initially, lo provides a more comprehensive toolkit for functional programming in Go. The choice between them depends on project requirements, team familiarity, and the desired balance between simplicity and feature richness.

4,744

A modern Go utility library which provides helpers (map, find, contains, filter, ...)

Pros of go-funk

  • More actively maintained with recent updates
  • Broader range of utility functions
  • Better performance for some operations

Cons of go-funk

  • Relies heavily on reflection, which can impact performance
  • Less idiomatic Go code compared to go-underscore
  • Steeper learning curve due to more complex API

Code Comparison

go-funk:

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

go-underscore:

result := underscore.Filter([]int{1, 2, 3, 4}, func(x int, _ int) bool {
    return x%2 == 0
})

Both libraries provide similar functionality, but go-funk's API is slightly more concise. However, go-underscore's approach is more aligned with idiomatic Go, including an index parameter in the filter function.

go-funk offers a wider range of utility functions and is more actively maintained, making it a good choice for projects requiring extensive functional programming capabilities. On the other hand, go-underscore may be preferable for developers seeking a more Go-like approach and potentially better performance in certain scenarios due to less reliance on reflection.

1,940

🍕 Enjoy a slice! A utility library for dealing with slices and maps that focuses on type safety and performance.

Pros of pie

  • More actively maintained with recent updates
  • Supports generics for type-safe operations
  • Offers a wider range of functions and utilities

Cons of pie

  • Requires code generation, which adds complexity to the build process
  • May have a steeper learning curve due to its more extensive API

Code Comparison

pie:

// Generate pie functions for []int
//go:generate pie int

go-underscore:

result := underscore.Map([]int{1, 2, 3}, func(n int) int {
    return n * 2
})

Key Differences

  • pie uses code generation to create type-specific functions, while go-underscore relies on interface{} for generic operations
  • pie offers better type safety and performance due to its generated code approach
  • go-underscore provides a simpler API that may be easier to use for basic operations

Use Cases

  • pie is well-suited for projects requiring type-safe operations and extensive utility functions
  • go-underscore is a good choice for simpler projects or when quick prototyping is needed

Community and Support

  • pie has more stars and contributors on GitHub, indicating a larger community
  • go-underscore has been around longer but has less recent activity
4,393

A comprehensive, efficient, and reusable util function library of Go.

Pros of lancet

  • More comprehensive and actively maintained library with a wider range of utility functions
  • Better organized into distinct packages for different types of operations (e.g., algorithm, datastructure, formatter)
  • Includes additional features like concurrent utilities and network-related functions

Cons of lancet

  • Larger codebase and API surface, potentially more complex to learn and use
  • May include unnecessary functions for simpler projects, leading to increased binary size
  • Less focused on functional programming paradigms compared to go-underscore

Code comparison

go-underscore:

result := underscore.Map([]int{1, 2, 3}, func(n int) int {
    return n * 2
})

lancet:

result := maputil.Map([]int{1, 2, 3}, func(n int) int {
    return n * 2
})

Both libraries provide similar functionality for common operations like mapping, but lancet offers a more extensive set of utilities across various domains. While go-underscore focuses primarily on functional programming concepts, lancet provides a broader range of general-purpose functions for Go development. The choice between the two depends on the specific needs of your project and your preference for library size and scope.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Underscore.go

Move Fast; Optimize Late

A useful collection of Go utilities. Designed for programmer happiness.

TL;DR Sort-of like underscore.js, but for Go

API Documentation

GoDoc is WorkInProgress

:warning: Warning

This package is in heavy flux at the moment as I work to incorporate feedback from various sources.

:squirrel: Todo

  • godoc
  • contains
  • indexOf
  • worker pools
  • parallel each
  • parallel map with worker pool
  • refactor to make functions first parameter (eg Each func(func(A), []A))
  • handle maps & slices
  • all
  • any
  • none

Typed Functions


Any


Each


Each func(func(A int), []A) Each func(func(A B), []A)

Applies the given iterator function to each element of a collection (slice or map).

If the collection is a Slice, the iterator function arguments are value, index

If the collection is a Map, the iterator function arguments are value, key

EachP is a Parallel implementation of Each and concurrently applies the given iterator function to each element of a collection (slice or map).

  // var Each func(func(value interface{}, i interface{}), interface{})

  var buffer bytes.Buffer

  fn := func(s, i interface{}) {
    buffer.WriteString(s.(string))
  }

  s := []string{"a", "b", "c", "d", "e"}
  Each(fn, s)

  expect := "abcde"

  e := un.Each(fn, s)

  fmt.Printf("%#v\n", e) //"abcde"

Typed Each can be defined using a function type and the MakeEach helper.

Using a Typed Slice

  var EachInt func(func(value, i int), []int)
  MakeEach(&EachInt)

  var sum int

  fn := func(v, i int) {
    sum += v
  }

  i := []int{1, 2, 3, 4, 5}
  EachInt(fn, i)

  fmt.Printf("%#v\n", sum) //15

Using a Typed Map

  var EachStringInt func(func(key string, value int), map[string]int)
  var sum int

  fn := func(v int, k string) {
    sum += v
  }

  m := map[string]int{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}
  EachStringInt(fn, m)

  fmt.Printf("%#v\n", sum) //15

Of note is the ability to close over variables within the calling scope.

Every


Map


Map func([]A, func(A) B) []B

Applies the given function to each element of a slice, returning a slice of results

The base Map function accepts interface{} types and returns []interface{}

  // Map func(interface{}, func(interface{}) interface{}) []interface{}

  s := []string{"a", "b", "c", "d"}

  fn := func(s interface{}) interface{} {
    return s.(string) + "!"
  }

  m := un.Map(ToI(s), fn)
  fmt.Println(m) //["a!", "b!", "c!", "d!"]

Typed Maps can be defined using a function type and the MakeMap helper.

  Map func([]A, func(A) B) []B

  var SMap func([]string, func(string) string) []string
  un.MakeMap(&SMap)

  m := un.SMap(s, fn)
  fmt.Println(m) //["a!", "b!", "c!", "d!"]

Of note is the return value of Map is a slice of the return type of the applied function.

Partition


Partition func([]A, func(A) bool) ([]A []A)

Partition splits a slice or map based on the evaluation of the supplied function

The base Partition function accepts interface{} types and returns []interface{}

  // Partition func(interface{}, func(interface{}) bool) ([]interface{}, []interface{})

  s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

  fn := func(i interface{}) bool {
    return (i.(int) % 2) == 1
  }

  odd, even := un.Partition(s, fn)

  fmt.Println(odd)  //[1, 3, 5, 7, 9]
  fmt.Println(even) //[2, 4, 6, 8, 10]

Typed Partitions can be defined using a function type and the MakePartition helper.

  // Partition func([]A, func(A) bool) ([]A []A)

  var IPartition func([]int, func(int) bool) ([]int, []int)

  un.MakePartition(&IPartition)

  s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

  fn := func(i int) bool {
    return (i % 2) == 1
  }

  odd, even := un.IPartition(s, fn)

  fmt.Println(odd)  //[1, 3, 5, 7, 9]
  fmt.Println(even) //[2, 4, 6, 8, 10]

Contains returns true if an object is in a slice.

  o := "a"
  s := []string{"a", "b", "c"}

  b := un.Contains(s, o)
  fmt.Println(b) //true

ToI converts a slice of arbitrary type []T into a slice of []interfaces{}

  s := []int{1, 1, 3, 5, 8, 13}
  i := un.ToI(s)

Notes

I am aware that the whole idea is not particularly very TheGoWay™, but it is useful as a learning exercise, and it is useful for moving fast and optimising later.