SoFunction
Updated on 2025-04-05

Go language uses generic encapsulation common map operations

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.

Go generics

The introduction of generics in version 1.18 is an important milestone in the development of Go, which greatly enhances the expressiveness and flexibility of the language.

Common Map Operations Based on Generics

In the previous article, we created it using Go genericsAn elegant slicing tool library, This blog will use Go generics to encapsulate common Map operations and accompany corresponding unit tests. 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. Get all keys of map

func GetMapKeys[K comparable, V any](m map[K]V) []K {
    keys := make([]K, 0, len(m))
    for k := range m {
       keys = append(keys, k)
    }
    return keys
}

2. Get map keys according to filter conditions

func GetMapKeysMatchCondition[K comparable, V any](m map[K]V, condition func(K, V) bool) []K {
    keys := make([]K, 0, len(m))
    for k, v := range m {
       if condition(k, v) {
          keys = append(keys, k)
       }
    }
    return keys
}

3. Get all values ​​of map

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

4. Get the values ​​of maps according to filtering conditions

func GetMapValuesMatchCondition[K comparable, V any](m map[K]V, condition func(K, V) bool) []V {
    values := make([]V, 0, len(m))
    for k, v := range m {
       if condition(k, v) {
          values = append(values, v)
       }
    }
    return values
}

5. Merge multiple maps

func MergeMaps[K comparable, V any](maps ...map[K]V) map[K]V {
    mergeMap := make(map[K]V)
    for _, m := range maps {
       for k, v := range m {
          mergeMap[k] = v
       }
    }
    return mergeMap
}

Turn slices

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
}

7. Slice to map

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
}

8. Copy the map

func CopyMap[K comparable, V any](oldMap map[K]V) map[K]V {
    newMap := make(map[K]V, len(oldMap))
    for k, v := range oldMap {
       newMap[k] = v
    }
    return newMap
}

Turn to map

func SyncMapToMap[K comparable, V any](syncMap ) map[K]V {
    m := make(map[K]V)
    (func(key, value any) bool {
       // Try to convert key and value to the specified type       k, ok1 := key.(K)
       v, ok2 := value.(V)
       if ok1 && ok2 {
          m[k] = v
       }
       return true
    })
    return m
}

change

func MapToSyncMap[K comparable, V any](m map[K]V) * {
    syncMap := &{}
    if m == nil {
       return syncMap
    }
    for k, v := range m {
       (k, v)
    }
    return syncMap
}

Code collection

I integrated all the above code into oneThe file is equipped with unit test filesmaps_test.go. If you guys need your project, just copy the following code to your project's basic code tool library.

package maps

import (
    "sync"
)

// Get all keys of mapfunc GetMapKeys[K comparable, V any](m map[K]V) []K {
    keys := make([]K, 0, len(m))
    for k := range m {
       keys = append(keys, k)
    }
    return keys
}

// Get map keys according to filter conditionsfunc GetMapKeysMatchCondition[K comparable, V any](m map[K]V, condition func(K, V) bool) []K {
    keys := make([]K, 0, len(m))
    for k, v := range m {
       if condition(k, v) {
          keys = append(keys, k)
       }
    }
    return keys
}

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

// Get the values ​​of map according to filtering conditionsfunc GetMapValuesMatchCondition[K comparable, V any](m map[K]V, condition func(K, V) bool) []V {
    values := make([]V, 0, len(m))
    for k, v := range m {
       if condition(k, v) {
          values = append(values, v)
       }
    }
    return values
}

// Merge multiple mapsfunc MergeMaps[K comparable, V any](maps ...map[K]V) map[K]V {
    mergeMap := make(map[K]V)
    for _, m := range maps {
       for k, v := range m {
          mergeMap[k] = v
       }
    }
    return mergeMap
}

// Map 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
}

// Slice to mapfunc 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
}

// Copy mapfunc CopyMap[K comparable, V any](oldMap map[K]V) map[K]V {
    newMap := make(map[K]V, len(oldMap))
    for k, v := range oldMap {
       newMap[k] = v
    }
    return newMap
}

// Turn to mapfunc SyncMapToMap[K comparable, V any](syncMap ) map[K]V {
    m := make(map[K]V)
    (func(key, value any) bool {
       // Try to convert key and value to the specified type       k, ok1 := key.(K)
       v, ok2 := value.(V)
       if ok1 && ok2 {
          m[k] = v
       }
       return true
    })
    return m
}

func MapToSyncMap[K comparable, V any](m map[K]V) * {
    syncMap := &{}
    if m == nil {
       return syncMap
    }
    for k, v := range m {
       (k, v)
    }
    return syncMap
}

maps_test.go

package maps

import (
    "sync"
    "testing"
)

// Test the GetMapKeys functionfunc TestGetMapKeys(t *) {
    m := map[string]int{
       "apple":  1,
       "banana": 2,
       "cherry": 3,
    }
    keys := GetMapKeys(m)
    if len(keys) != len(m) {
       ("Expected %d keys, got %d", len(m), len(keys))
    }
    for _, k := range keys {
       if _, exists := m[k]; !exists {
          ("Key %s not found in original map", k)
       }
    }
}

// Test the GetMapKeysMatchCondition functionfunc TestGetMapKeysMatchCondition(t *) {
    m := map[string]int{
       "apple":  1,
       "banana": 2,
       "cherry": 3,
    }
    condition := func(k string, v int) bool {
       return v > 1
    }
    keys := GetMapKeysMatchCondition(m, condition)
    for _, k := range keys {
       if m[k] <= 1 {
          ("Key %s should not be included as its value is not greater than 1", k)
       }
    }
}

// Test the GetMapValues ​​functionfunc TestGetMapValues(t *) {
    m := map[string]int{
       "apple":  1,
       "banana": 2,
       "cherry": 3,
    }
    values := GetMapValues(m)
    if len(values) != len(m) {
       ("Expected %d values, got %d", len(m), len(values))
    }
    valueSet := make(map[int]bool)
    for _, v := range values {
       valueSet[v] = true
    }
    for _, v := range m {
       if !valueSet[v] {
          ("Value %d not found in result values", v)
       }
    }
}

// Test the GetMapValuesMatchCondition functionfunc TestGetMapValuesMatchCondition(t *) {
    m := map[string]int{
       "apple":  1,
       "banana": 2,
       "cherry": 3,
    }
    condition := func(k string, v int) bool {
       return v > 1
    }
    values := GetMapValuesMatchCondition(m, condition)
    for _, v := range values {
       if v <= 1 {
          ("Value %d should not be included as it is not greater than 1", v)
       }
    }
}

// Test the MergeMaps functionfunc TestMergeMaps(t *) {
    m1 := map[string]int{
       "apple":  1,
       "banana": 2,
    }
    m2 := map[string]int{
       "banana": 3,
       "cherry": 4,
    }
    merged := MergeMaps(m1, m2)
    for k := range m1 {
       if _, exists := merged[k]; !exists {
          ("key %s not exist in m1", k)
       }
    }
    for k := range m2 {
       if _, exists := merged[k]; !exists {
          ("key %s not exist in m2", k)
       }
    }
}

// Test the MapToSlice functionfunc TestMapToSlice(t *) {
    m := map[string]int{
       "apple":  1,
       "banana": 2,
       "cherry": 3,
    }
    extractor := func(v int) int {
       return v * 2
    }
    slice := MapToSlice(m, extractor)
    if len(slice) != len(m) {
       ("Expected %d elements in slice, got %d", len(m), len(slice))
    }
    for _, v := range m {
       found := false
       for _, s := range slice {
          if s == v*2 {
             found = true
             break
          }
       }
       if !found {
          ("Transformed value %d not found in slice", v*2)
       }
    }
}

// Test SliceToMap functionfunc TestSliceToMap(t *) {
    s := []int{1, 2, 3}
    keyFunc := func(v int) int {
       return v * 10
    }
    m := SliceToMap(s, keyFunc)
    if len(m) != len(s) {
       ("Expected %d keys in map, got %d", len(s), len(m))
    }
    for _, v := range s {
       key := keyFunc(v)
       if val, exists := m[key]; !exists || val != v {
          ("Value for key %d in map does not match original slice", key)
       }
    }
}

// Test CopyMap functionfunc TestCopyMap(t *) {
    oldMap := map[string]int{
       "apple":  1,
       "banana": 2,
       "cherry": 3,
    }
    newMap := CopyMap(oldMap)
    if len(newMap) != len(oldMap) {
       ("Expected %d keys in new map, got %d", len(oldMap), len(newMap))
    }
    for k, v := range oldMap {
       if val, exists := newMap[k]; !exists || val != v {
          ("Value for key %s in new map does not match original map", k)
       }
    }
}

// Test SyncMapToMap functionfunc TestSyncMapToMap(t *) {
    var syncMap 
    ("apple", 1)
    ("banana", 2)
    ("cherry", 3)
    m := SyncMapToMap[string, int](syncMap)
    (func(key, value any) bool {
       k := key.(string)
       v := value.(int)
       if val, exists := m[k]; !exists || val != v {
          ("Value for key %s in map does not match ", k)
       }
       return true
    })
}

// Test the MapToSyncMap functionfunc TestMapToSyncMap(t *) {
    m := map[string]int{
       "apple":  1,
       "banana": 2,
       "cherry": 3,
    }
    syncMap := MapToSyncMap(m)
    for k, v := range m {
       value, exists := (k)
       if !exists || value.(int) != v {
          ("Value for key %s in  does not match original map", k)
       }
    }
}

Summarize

This article uses Go generics to encapsulate common Map operations and compiles a Map tool library

The above is the detailed content of the common Map operations in Go using generic encapsulation. For more information about Go generic encapsulation Map operations, please pay attention to my other related articles!