SoFunction
Updated on 2025-04-11

Go language generics create an elegant slicing tool library

What is a generic

Generics are programming paradigms that allow developers to define common type parameters rather than specific types when writing code. Generics allow you to write code that can handle multiple data types without repeatedly writing the same logic for each type. For example, a generic function can process multiple types of data such as integers, floating point numbers, strings, etc. at the same time.

What problems does generics solve

Before the introduction of generics in Go language, developers often needed to write duplicate code when dealing with different data types. For example, implementing a sorting algorithm may require writing different versions for integers, floating point numbers, strings, etc. This repetition not only increases the amount of code, but also reduces the maintainability of the code. After introducing generics, you can define a common type parameter and write a general sorting function to improve the reusability and maintainability of the code.

Common slicing operations based on generics

Based on his own experience in actual development, the blogger will use Go generics to encapsulate some common slice operations. All the codes written in this blog are OKPublic code library directly integrated into production environmentsmiddle. Dear friends, you can migrate the code that is helpful to your project to your own project based on the actual situation of your project.

1. Invert the slice (change the original slice)

func ReverseOriginalSlice[T any](s []T) {
    for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
       s[i], s[j] = s[j], s[i]
    }
}

2. Invert the slice (not changing the original slice)

func ReverseSlice[T any](s []T) []T {
    res := make([]T, len(s))
    copy(res, s)
    ReverseOriginalSlice(res) // Call the ReverseOriginalSlice function before    return res
}

3. Slice in batches

func BatchSlice[T any](s []T, size int) [][]T {
    var batchSlice [][]T
    // traverse the slice, and take size elements each time    for i := 0; i &lt; len(s); i += size {
       end := i + size
       // Handle the situation where the last batch of elements is insufficient in size       if end &gt; len(s) {
          end = len(s)
       }
       // Add the elements of the current batch to the result       batchSlice = append(batchSlice, s[i:end])
    }
    return batchSlice
}

4. Merge slices

func MergeSlices[T any](slices ...[]T) []T {
    totalLength := 0
    for _, slice := range slices {
       totalLength += len(slice)
    }
    res := make([]T, 0, totalLength)
    for _, slice := range slices {
       ls := make([]T, len(slice))
       copy(ls, slice)
       res = append(res, ls...)
    }
    return res
}

5. Slice and remove heavy

func UniqueSlice[T comparable](s []T) []T {
    seen := make(map[T]bool)
    res := make([]T, 0, len(s))
    for _, v := range s {
       if !seen[v] {
          // If the element has not appeared, add to the result slice          res = append(res, v)
          seen[v] = true
       }
    }
    return res
}

6. Slice to hash table

func SliceToMap[T any, K comparable](s []T, keyFunc func(T) K) map[K]T {
    res := make(map[K]T)
    for _, v := range s {
       key := keyFunc(v)
       res[key] = v
    }
    return res
}

7. To slice hash table

func MapToSlice[K comparable, V any, T any](m map[K]V, extractor func(V) T) []T {
    res := make([]T, 0, len(m))
    for _, v := range m {
       res = append(res, extractor(v))
    }
    return res
}

8. Get a field of the slice element

func GetListField[T any, V any](s []T, fieldFunc func(T) V) []V {
    res := make([]V, 0, len(s))
    for _, item := range s {
       res = append(res, fieldFunc(item))
    }
    return res
}

9. Slicing all elements meet the conditions

func SliceMatchCondition[T any](s []T, condition func(T) bool) bool {
    for _, v := range s {
       if !condition(v) {
          return false
       }
    }
    return true
}

10. Take slices and intersect

func Intersection[T comparable](slices ...[]T) []T {
    if len(slices) == 0 {
       return nil
    }

    // Use map to store elements in the first slice    intersectionMap := make(map[T]int)
    for _, v := range slices[0] {
       intersectionMap[v]++
    }

    // traverse the subsequent slices and update the intersection    for _, slice := range slices[1:] {
       m := make(map[T]int)
       for _, v := range slice {
          if _, exists := intersectionMap[v]; exists {
             m[v]++
          }
       }
       intersectionMap = m
    }

    // Collect the intersection elements into the result slice    var res []T
    for k := range intersectionMap {
       res = append(res, k)
    }

    return res
}

11. Take slices and gather

func Union[T comparable](slices ...[]T) []T {
    elementMap := make(map[T]struct{})
    for _, slice := range slices {
       for _, v := range slice {
          elementMap[v] = struct{}{}
       }
    }

    var res []T
    for k := range elementMap {
       res = append(res, k)
    }

    return res
}

Code collection

I integrated all the above code into onein the file. If you guys need your project, just copy the following code to your project's basic code tool library.

package slices

// Invert the slice (change the original slice)func ReverseOriginalSlice[T any](s []T) {
    for i, j := 0, len(s)-1; i &lt; j; i, j = i+1, j-1 {
       s[i], s[j] = s[j], s[i]
    }
}

// Back-drilling slice (no change the original slice)func ReverseSlice[T any](s []T) []T {
    res := make([]T, len(s))
    copy(res, s)
    ReverseOriginalSlice(res)  
    return res
}

// Slice in batchesfunc BatchSlice[T any](s []T, size int) [][]T {
    var batchSlice [][]T
    
    for i := 0; i &lt; len(s); i += size {
       end := i + size
  
       if end &gt; len(s) {
          end = len(s)
       }
       
       batchSlice = append(batchSlice, s[i:end])
    }
    return batchSlice
}

// Merge slicesfunc MergeSlices[T any](slices ...[]T) []T {
    totalLength := 0
    for _, slice := range slices {
       totalLength += len(slice)
    }
    res := make([]T, 0, totalLength)
    for _, slice := range slices {
       ls := make([]T, len(slice))
       copy(ls, slice)
       res = append(res, ls...)
    }
    return res
}

// Slice and remove heavyfunc UniqueSlice[T comparable](s []T) []T {
    seen := make(map[T]bool)
    res := make([]T, 0, len(s))
    for _, v := range s {
       if !seen[v] {
          res = append(res, v)
          seen[v] = true
       }
    }
    return res
}

// Slice to hash tablefunc SliceToMap[T any, K comparable](s []T, keyFunc func(T) K) map[K]T {
    res := make(map[K]T)
    for _, v := range s {
       key := keyFunc(v)
       res[key] = v
    }
    return res
}

// hash table to slicefunc MapToSlice[K comparable, V any, T any](m map[K]V, extractor func(V) T) []T {
    res := make([]T, 0, len(m))
    for _, v := range m {
       res = append(res, extractor(v))
    }
    return res
}

// Get a field of the slice elementfunc GetListField[T any, V any](s []T, fieldFunc func(T) V) []V {
    res := make([]V, 0, len(s))
    for _, item := range s {
       res = append(res, fieldFunc(item))
    }
    return res
}

// All elements of the slice meet the conditionsfunc SliceMatchCondition[T any](s []T, condition func(T) bool) bool {
    for _, v := range s {
       if !condition(v) {
          return false
       }
    }
    return true
}

// Get slices and intersectfunc Intersection[T comparable](slices ...[]T) []T {
    if len(slices) == 0 {
       return nil
    }

    intersectionMap := make(map[T]int)
    for _, v := range slices[0] {
       intersectionMap[v]++
    }

    for _, slice := range slices[1:] {
       m := make(map[T]int)
       for _, v := range slice {
          if _, exists := intersectionMap[v]; exists {
             m[v]++
          }
       }
       intersectionMap = m
    }

    var res []T
    for k := range intersectionMap {
       res = append(res, k)
    }

    return res
}

// Take slices and gatherfunc Union[T comparable](slices ...[]T) []T {
    elementMap := make(map[T]struct{})
    for _, slice := range slices {
       for _, v := range slice {
          elementMap[v] = struct{}{}
       }
    }

    var res []T
    for k := range elementMap {
       res = append(res, k)
    }

    return res
}

Summarize

This article uses Go generics to encapsulate common slice operations and organizes a slice tool library

This is the end of this article about creating an elegant slicing tool library for Go generics. For more related Go generic content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!