Golang泛型:分享几个常用的函数

402 阅读3分钟

引言

Go 1.18 版本引入了泛型支持,这为我们的代码带来了更多的灵活性和复用性。本文将介绍如何利用 Go 1.18 的泛型特性,创建一个高效且实用的工具函数库。

为什么需要泛型?

在没有泛型的情况下,我们经常需要为不同类型的切片编写类似的函数,这会导致代码冗余和维护困难。泛型允许我们编写通用的函数,处理多种类型的数据,从而减少重复代码,提高代码的可读性和可维护性。

工具函数库介绍

我们将创建一个名为 tools 的包,其中包含以下几个常用的工具函数:

  1. InSlice:检查元素是否存在于切片中。
  2. SliceToMapByField:将对象切片转换为 map,键值对为指定字段和对象。
  3. SliceToMapsByField:将对象切片转换为 map 数组,键值对为指定字段和对象数组。
  4. DiffLists:比较两个切片,返回新增、删除和共同拥有的元素。

1. InSlice

InSlice 函数用于检查一个元素是否存在于切片中。通过泛型的支持,我们可以轻松地处理不同类型的切片。

package tools

// InSlice 检查元素是否存在于切片中
func InSlice[K comparable](slice []K, key K) bool {
	for _, v := range slice {
		if v == key {
			return true
		}
	}
	return false
}

2. SliceToMapByField

SliceToMapByField 函数将对象切片转换为 map,键值对为指定字段和对象。这个函数在处理数据转换时非常有用,特别是在处理数据库查询结果时。

package tools

// SliceToMapByField 将对象切片转换为 map,键值对为指定字段和对象
func SliceToMapByField[T any, K comparable](slice []T, keyFunc func(T) K) map[K]T {
	result := make(map[K]T)
	for _, v := range slice {
		key := keyFunc(v)
		result[key] = v
	}
	return result
}

3. SliceToMapsByField

SliceToMapsByField 函数将对象切片转换为 map 数组,键值对为指定字段和对象数组。这个函数在处理多对一关系的数据时非常有用。

package tools

// SliceToMapsByField 将对象切片转换为 map 数组,键值对为指定字段和对象数组
func SliceToMapsByField[T any, K comparable](slice []T, fieldFunc func(T) K) map[K][]T {
	result := make(map[K][]T)
	for _, v := range slice {
		key := fieldFunc(v)
		result[key] = append(result[key], v)
	}
	return result
}

4. DiffLists

DiffLists 函数接收两个切片 a 和 b,返回三个切片: 第一个切片包含 b 中但不在 a 中的元素,用于新增。 第二个切片包含 a 中但不在 b 中的元素,用于删除。 第三个切片包含 a 和 b 共同拥有的元素,用于编辑。

package tools

// DiffLists 比较两个切片,返回新增、删除和共同拥有的元素
func DiffLists[T comparable](a, b []T) (added, deleted, edited []T) {
	bMap := make(map[T]bool)
	for _, item := range b {
		bMap[item] = true
	}

	for _, item := range a {
		if _, found := bMap[item]; found {
			edited = append(edited, item)
		} else {
			deleted = append(deleted, item)
		}
	}

	for item := range bMap {
		if !InSlice(a, item) {
			added = append(added, item)
		}
	}

	return added, deleted, edited
}

使用示例

下面是一些使用这些工具函数的示例代码,帮助你更好地理解和应用它们。

package main

import (
	"fmt"
	"your_project/tools"
)

type User struct {
	ID   int
	Name string
}

func main() {
	// InSlice 示例
	intSlice := []int{1, 2, 3, 4, 5}
	fmt.Println(tools.InSlice(intSlice, 3)) // 输出: true

	// SliceToMapByField 示例
	users := []User{
		{ID: 1, Name: "Alice"},
		{ID: 2, Name: "Bob"},
		{ID: 3, Name: "Charlie"},
	}
	userMap := tools.SliceToMapByField(users, func(u User) int { return u.ID })
	fmt.Println(userMap) // 输出: map[1:{1 Alice} 2:{2 Bob} 3:{3 Charlie}]

	// SliceToMapsByField 示例
	usersWithSameID := []User{
		{ID: 1, Name: "Alice"},
		{ID: 1, Name: "Alice2"},
		{ID: 2, Name: "Bob"},
	}
	userMaps := tools.SliceToMapsByField(usersWithSameID, func(u User) int { return u.ID })
	fmt.Println(userMaps) // 输出: map[1:[{1 Alice} {1 Alice2}] 2:[{2 Bob}]]

	// DiffLists 示例
	listA := []int{1, 2, 3, 4}
	listB := []int{3, 4, 5, 6}
	added, deleted, edited := tools.DiffLists(listA, listB)
	fmt.Println("Added:", added)    // 输出: Added: [5 6]
	fmt.Println("Deleted:", deleted) // 输出: Deleted: [1 2]
	fmt.Println("Edited:", edited)   // 输出: Edited: [3 4]
}

如果你觉得这篇文章对你有帮助,不妨点个赞,让更多的人看到它吧!