lo: Lodash-inspired utility library for Go

repository·master·Indexed 12 days ago

https://github.com/samber/lo

A Lodash-inspired utility library for Go 1.18+ that leverages generics to provide high-level functional programming helpers for slices, maps, channels, and more. It includes synchronous helpers, parallel processing via the parallel package, and mutable operations via the mutable package. Features include a wide range of helpers for filtering, mapping, reducing, and searching, as well as an experimental SIMD package requiring Go 1.26+.

Tokens
185K
Snippets
795
Records
854
Agent score
97%

What's inside lo

  1. Overview of samber/lo

    master

    samber/lo is a Lodash-style utility library for Go, built on Go 1.18+ Generics. It provides synchronous helpers to simplify working with slices, maps, strings, channels, and functions.

    While it overlaps with the Go standard library's slices and maps packages in some areas, it provides a broader set of abstractions for common coding tasks.

  2. Use NewDebounceBy vs NewDebounce

    master

    When choosing between debounce implementations in lo:

    • NewDebounce: Applies a single debounce timer to all calls. If any call is made, the timer resets for everyone.
    • NewDebounceBy: Applies a separate debounce timer for every unique key provided. Activity on key_A does not reset or delay the timer for key_B.
  3. Choose between immutable and mutable operations

    master

    When using lo, you should choose your approach based on your performance and safety requirements:

    • Immutable Operations (Default): Most functions return new collections. This is safer for concurrent programming and prevents side effects when sharing data.
    • Mutable Operations: These modify collections in-place. While they are more memory efficient, they are less safe in concurrent scenarios or when multiple parts of your code share the same data.
  4. Related slice trimming functions

    master

    In addition to lo.Trim, the lo library provides specialized trimming functions for more granular control:

    • lo.TrimLeft: Removes elements only from the beginning of the collection.
    • lo.TrimRight: Removes elements only from the end of the collection.
    • lo.TrimPrefix: Removes a specific prefix from a collection.
    • lo.TrimSuffix: Removes a specific suffix from a collection.
  5. Untitled record

    master

    The lo library is built on four core principles to provide a Lodash-like experience in Go:

    1. Type Safety Through Generics: Built on Go 1.18+ generics, ensuring compile-time type safety and eliminating the need for runtime type assertions.
    2. Immutable by Default: The primary lo package returns new collections instead of modifying the input, making code more predictable.
    3. Performance Specialization: When immutability or single-threaded execution is not ideal, use specialized packages:
      • lo/mutable for in-place modifications.
      • lo/parallel for concurrent processing.
      • lo/it for lazy evaluation via Go 1.23+ iterators.
    4. Minimal Dependencies: Zero external dependencies beyond the Go standard library.
  6. Use KeyBy vs similar helpers

    master

    Depending on your specific needs, you might want to use a variation of KeyBy:

    • lo.KeyByErr: Use this if your iteratee function can return an error. It returns the map and an error if the iteratee fails.
    • lo.GroupBy: Use this if you want to group multiple items under the same key (returns map[K][]V) instead of having a single item per key.
    • lo.GroupByErr: A version of GroupBy that handles errors from the iteratee.
    • lo.PartitionBy: Use this to split a slice into two slices based on a predicate.
    • lo.Associate: A map-based alternative for creating associations.
  7. Use the fluent If/Else conditional builder

    master

    The lo library provides a fluent API for constructing conditional chains (If/ElseIf/Else) that are more readable than nested ternary-like structures. You can start a chain with lo.If or lo.IfF and extend it with ElseIf or ElseIfF before concluding with Else or ElseF to return the final value.

    Use the standard versions (If, ElseIf, Else) when the results are simple values. Use the function-based versions (IfF, ElseIfF, ElseF) when the result requires expensive computation, as these versions evaluate the provided function lazily (only if the condition is met).

    // Standard chain
    result := lo.If(false, 1).ElseIf(false, 2).Else(3)
    // 3
    
    // Lazy chain using functions
    result := lo.IfF(true, func() int {
        return 1
    }).Else(3)
    // 1
  8. Performance characteristics of SIMD helpers

    master

    SIMD operators in exp/simd exhibit different performance profiles depending on the dataset size.

    • Small datasets: SIMD operations are generally slower than the standard fallback implementations due to the overhead of SIMD setup.
    • Large datasets: SIMD operations are significantly faster than the fallback implementations, with performance improvements scaling with the width of the instruction set (AVX, AVX2, or AVX512).
    BenchmarkSumInt8/small/Fallback-lo-4             203616572        5.875 ns/op
    BenchmarkSumInt8/small/AVX-x16-4                 100000000        12.04 ns/op
    BenchmarkSumInt8/small/AVX2-x32-4                 64041816        17.93 ns/op
    BenchmarkSumInt8/small/AVX512-x64-4               26947528        44.75 ns/op
    
    BenchmarkSumInt8/xlarge/Fallback-lo-4               247677       4860 ns/op
    BenchmarkSumInt8/xlarge/AVX-x16-4                  3851040      311.4 ns/op
    BenchmarkSumInt8/xlarge/AVX2-x32-4                  7100002      169.2 ns/op
    BenchmarkSumInt8/xlarge/AVX512-x64-4              10107534      118.1 ns/op
  9. OmitByErr vs OmitBy

    master

    Use OmitByErr when your predicate function needs to return an error. If your predicate only needs to return a bool to decide whether to omit an entry, use lo.OmitBy instead.

    OmitByErr is specifically designed for cases where the filtering process itself might encounter a failure that should halt the entire operation.

  10. Understand functional programming terms in lo

    master

    The lo library is built using functional programming principles. Understanding these core terms will help you use the API correctly:

    • Predicate Function: A function that returns a bool. Used for testing conditions in functions like Filter, Find, and Contains.
    • Transformer Function: A function that converts one value into another. Used in operations like Map and MapValues.
    • Reducer Function: A function that combines two values into a single accumulated value. Used with Reduce.
    • Comparator Function: A function that compares two values to determine their relative order. Used for sorting.
    • Higher-Order Functions: Most lo utilities are higher-order functions, meaning they accept other functions (like predicates or transformers) as arguments.
    • Lazy Evaluation: Some operations use lazy evaluation to improve performance when working with large datasets.
    • Immutability: Most lo functions follow immutable patterns, meaning they return a new collection instead of modifying the original one.
  11. Use TryX for safe error and panic handling

    master

    The TryX family of functions allows you to execute a callback and safely handle both returned errors and runtime panics. If the callback returns an error or triggers a panic, the Try function returns false. Otherwise, it returns true.

    This pattern is useful for simplifying control flow when you want to attempt an operation and proceed only if it succeeds without manually checking if err != nil or using recover() for panics.

    // Returns false if the function returns an error
    ok := lo.Try(func() error {
        return fmt.Errorf("boom")
    })
    // ok == false
    
    // Returns false if the function panics
    ok = lo.Try0(func() {
        panic("boom")
    })
    // ok == false
    
    // Returns true if the function succeeds
    ok = lo.Try2(func() (int, error) {
        return 42, nil
    })
    // ok == true
  12. Untitled record

    master

    Install the lo library using go get. The library is currently at v1 and follows Semantic Versioning (SemVer) strictly. It has zero dependencies outside of the Go standard library.

    To install the v1 branch:

    go get -u github.com/samber/lo@v1