Golang Go语言中如何写一个通用的方法,将一个结构体切片,根据id字段,转为map结构

发布于 1周前 作者 zlyuanteng 来自 Go语言

rt

比如:

定义结构体

    type S struct {
        ID int64
        Name string
    }

有切片

[]S{
	{
    	ID: 1,
        Name: "1",
    },
	{
    	ID: 2,
        Name: "2",
    },
}

需要转为

map[int64]S {
	1: {
   	ID: 1,
       Name: "1",
   },
   2: {
   	ID: 2,
       Name: "2",
   },
}

当然,要求这个方法是通用的


Golang Go语言中如何写一个通用的方法,将一个结构体切片,根据id字段,转为map结构

更多关于Golang Go语言中如何写一个通用的方法,将一个结构体切片,根据id字段,转为map结构的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

22 回复

刚发现别的帖子哈哈哈哈 现学现卖
https://www.v2ex.com/t/917480#reply0
https://aicodehelper.com/tools/index.html#/#v2ex
答:
// 将一个结构体切片,根据 id 字段,转为 map 结构
func SliceToMap(s []S) map[int64]S {
m := make(map[int64]S)
for _,v := range s {
m[v.ID] = v
}
return m
} 😝 😝 😝

更多关于Golang Go语言中如何写一个通用的方法,将一个结构体切片,根据id字段,转为map结构的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


遍历 slice 转为 map ,取出 ID 放新 map

<br>func ToMap[T interface{}](slice []T, getID func(T) string) map[string]T {<br> if len(slice) == 0 {<br> return nil<br> }<br> mp := make(map[string]T, len(slice))<br> for _, obj := range slice {<br> id := getID(obj)<br> mp[id] = obj<br> }<br> return mp<br>}<br><br>type A struct {<br> ID string<br> Value string<br>}<br><br>func main() {<br> sliceA := []A{<br> {<br> ID: "1",<br> Value: "a",<br> },<br> {<br> ID: "2",<br> Value: "b",<br> },<br> {<br> ID: "3",<br> Value: "c",<br> },<br> }<br> result := ToMap(sliceA, func(t A) string {<br> return <a target="_blank" href="http://t.ID" rel="nofollow noopener">t.ID</a><br> })<br> fmt.Println(result)<br>}<br>

随手写的 大概是这个意思吧

  1. struct (marshal) -> json
    2. json (unmarshal)-> map

type Row[K any] interface {
GetID() K
}

func ToMap[K comparable, V Row[K]](rows []V) map[K]V {
var m = make(map[K]V, len(rows))
for i, v := range rows {
m[v.GetID()] = rows[i]
}
return m
}

<br>func convertSliceToMap(slice []MyStruct) map[int]MyStruct {<br> result := make(map[int]MyStruct)<br> for _, item := range slice {<br> result[<a target="_blank" href="http://item.id" rel="nofollow noopener">item.id</a>] = item<br> }<br> return result<br>}<br>

<br><br>func ToMap2(d []interface{}, getID func(interface{}) interface{}) map[interface{}]interface{} {<br> mp := make(map[interface{}]interface{}, len(d))<br> for _, o := range d {<br> mp[getID(o)] = o<br> }<br> return mp<br>}<br><br>

func SliceToMap(slice interface{}, fieldName string) interface{} {
// 检查 slice 是否为空
s := reflect.ValueOf(slice)
if s.Len() == 0 {
return reflect.MakeMap(reflect.MapOf(reflect.TypeOf(s.Index(0).FieldByName(fieldName).Interface()).Elem(), reflect.TypeOf(slice).Elem())).Interface()
}

// 获取结构体类型信息和 id 字段的索引值
st := s.Index(0).Type()
idIndex := -1
for i := 0; i < st.NumField(); i++ {
if st.Field(i).Name == fieldName {
idIndex = i
break
}
}
if idIndex == -1 {
panic(fmt.Sprintf(“Field %s not found in struct %s”, fieldName, st.Name()))
}

// 构建 map
m := reflect.MakeMap(reflect.MapOf(st.Field(idIndex).Type, st))
for i := 0; i < s.Len(); i++ {
key := s.Index(i).Field(idIndex)
value := s.Index(i)
m.SetMapIndex(key, value)
}
return m.Interface()
}

我尝试把 KV 都用泛型,发现它无法自动推导,使用起来很不方便。后来把 key 写成 any 。

使用方法是给需要转 map 的 struct 加一个取 key 的方法,然后就能调用函数一行转了。

go<br>// Mappable can be converted to a Map, and it is usually a struct with a unique primary key field.<br>// Please implement a method named UniqueKey() that returns a key that can be used in Map.<br>type Mappable interface {<br> UniqueKey() any<br>}<br><br>// ConvertToMap can convert an array of struct to map<br>func ConvertToMap[E Mappable](s []E) map[any]E {<br> m := make(map[any]E)<br> for i := range s {<br> m[s[i].UniqueKey()] = s[i]<br> }<br> return m<br>}<br>

type S struct {
ID int64
Name string
}

func (s S) PrimaryKey() int64 {
return s.ID
}

type PrimaryKey interface {
PrimaryKey() int64
}

func TestStructSliceToMap(t *testing.T) {
list := []PrimaryKey{S{ID: 1, Name: “string1”}, S{ID: 2, Name: “string2”}}
fmt.Println(StructSliceToMap(list))
}

func StructSliceToMap(list []PrimaryKey) (smap map[int64]PrimaryKey) {
smap = make(map[int64]PrimaryKey)
if len(list) == 0 {
return
}

for i := range list {
smap[list[i].PrimaryKey()] = list[i]

}
return
}

问了一下 ChatGPT
go<br> func structToMap(obj interface{}) (map[string]interface{}, error) {<br> <br> objMap := make(map[string]interface{})<br> <br> s, err := json.Marshal(&amp;obj)<br> <br> if err != nil {<br> <br> return nil, err<br> <br> }<br> <br> err = json.Unmarshal(s, &amp;objMap)<br> <br> if err != nil {<br> <br> return nil, err<br> <br> }<br> <br> return objMap, nil<br> <br> }<br>

func slice2map(input []S) map[int]S {
这不是随便写吗?
}

不知道你要的通用是啥意思:

func slice2map[T any](input []T) map[int]T {
泛型试试?
}

slice 里的 index 和元素的 ID 一定对应吗?不对应怎么处理?

<br>// You can edit this code!<br>// Click here and start typing.<br>package main<br><br>import "fmt"<br><br>type S struct {<br> ID int<br> Name string<br>}<br><br>func (s S) GetID() int {<br><br> return <a target="_blank" href="http://s.ID" rel="nofollow noopener">s.ID</a><br><br>}<br><br>type IS interface {<br> S<br> GetID() int<br>}<br><br>func slice2map[T IS](in []T) map[int]T {<br> out := map[int]T{}<br> for _, s := range in {<br> out[s.GetID()] = s<br> }<br> return out<br>}<br><br>func main() {<br> input := []S{{1, "a"}, {2, "b"}}<br> fmt.Println(slice2map(input))<br>}<br><br>

有谁能试一下,让 chatgpt 做这事

你的这个需求前面那几个简单的就可以了,如果要做复杂的映射,我一般这么写
如果你写过类似函数式的东西应该知道啥意思
go<br>func SliceToMap[U comparable, T, G any](s []T, keySelector func(int, T) U, valueMapper func(int, T) G) map[U]G {<br> m := make(map[U]G)<br> for i, v := range s {<br> m[keySelector(i, v)] = valueMapper(i, v)<br> }<br> return m<br>}<br>func Usage() {<br> type named struct {<br> Name string<br> Value string<br> }<br><br> type additional struct {<br> Age int<br> Name string<br> Value string<br> }<br><br> s := []named{{"1", "1"}, {"2", "2"}, {"3", "3"}}<br> targetMap := SliceToMap(s,<br> func(idx int, v named) string { return <a target="_blank" href="http://v.Name" rel="nofollow noopener">v.Name</a> },<br> func(idx int, v named) additional { return additional{idx, <a target="_blank" href="http://v.Name" rel="nofollow noopener">v.Name</a>, v.Value} })<br><br>}<br>


假设你有一个结构体类型为 Person ,其中有一个字段 id ,类型为 int ,表示人员的唯一标识符。你可以编写一个通用的方法,将一个[]Person 类型的切片,根据 id 字段,转换成一个 map[int]Person 类型的映射表。以下是一个示例实现:

type Person struct {
id int
name string
age int
}

func SliceToMapByID(slice interface{}) (map[int]interface{}, error) {
value := reflect.ValueOf(slice)
if value.Kind() != reflect.Slice {
return nil, errors.New(“input is not a slice”)
}

mapValue := reflect.MakeMapWithSize(reflect.MapOf(reflect.TypeOf(int(0)), value.Type().Elem()), value.Len())

for i := 0; i < value.Len(); i++ {
elem := value.Index(i)
id := elem.FieldByName(“id”).Interface().(int)
mapValue.SetMapIndex(reflect.ValueOf(id), elem)
}

return mapValue.Interface().(map[int]interface{}), nil
}

这个方法的输入参数是一个空接口类型,可以接受任何类型的切片作为输入。使用反射来处理输入参数的类型,并检查它是否为切片类型。如果是,就创建一个空的映射表,然后遍历切片中的每个元素,提取它的 id 字段的值,并将该元素添加到映射表中。最后返回一个 map[int]interface{}类型的映射表和一个 error 类型的错误(如果有的话)。

请注意,由于使用了反射,这个方法的性能可能不是非常高效,特别是对于大型切片来说。因此,如果你知道你的切片类型是[]Person ,你也可以直接编写一个特定类型的方法,而不是使用反射。

哈哈,这题我会,因为我之前写过,用的反射把 ID 取出来。如果用泛型的话,好像不能取出 ID 属性吧。除非有 ID() 方法

panic: interface conversion: interface {} is map[int]main.Person, not map[int]interface {}, 这里出问题了:mapValue.Interface().(map[int]interface{})

在Golang中,你可以编写一个通用的方法,将一个结构体切片根据某个字段(如id字段)转换为map结构。以下是一个示例代码,展示了如何实现这一功能:

package main

import (
	"fmt"
	"reflect"
)

func StructSliceToMapByField(slice interface{}, fieldName string) (map[interface{}]interface{}, error) {
	result := make(map[interface{}]interface{})
	s := reflect.ValueOf(slice)

	if s.Kind() != reflect.Slice {
		return nil, fmt.Errorf("input is not a slice")
	}

	for i := 0; i < s.Len(); i++ {
		elem := s.Index(i).Elem()
		fieldID := elem.FieldByName(fieldName)
		if !fieldID.IsValid() {
			return nil, fmt.Errorf("field %s not found", fieldName)
		}
		result[fieldID.Interface()] = elem.Interface()
	}

	return result, nil
}

func main() {
	type Person struct {
		ID   int
		Name string
	}

	people := []Person{{1, "Alice"}, {2, "Bob"}}
	m, err := StructSliceToMapByField(people, "ID")
	if err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(m)
	}
}

这个示例中,StructSliceToMapByField函数接受一个切片和一个字段名,返回一个map,其中键是字段的值,值是结构体本身。注意,该方法使用反射机制,因此有一定的性能开销。同时,要确保输入切片不为空且结构体包含指定的字段。

回到顶部