go 如何写一个通用的方法,将一个结构体切片,根据 id 字段,转为 map 结构
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",
},
}
当然,要求这个方法是通用的
感谢 @jitongxi 提供的方法
go.dev/play/p/WHa2CfMO3Lr
刚发现别的帖子哈哈哈哈 现学现卖
www.hesudu.com/t/917480#reply0
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
} 😝 😝 😝
遍历 slice 转为 map ,取出 ID 放新 map
func ToMap[T interface{}](slice []T, getID func(T) string) map[string]T {
if len(slice) == 0 {
return nil
}
mp := make(map[string]T, len(slice))
for _, obj := range slice {
id := getID(obj)
mp[id] = obj
}
return mp
}
type A struct {
ID string
Value string
}
func main() {
sliceA := []A{
{
ID: "1",
Value: "a",
},
{
ID: "2",
Value: "b",
},
{
ID: "3",
Value: "c",
},
}
result := ToMap(sliceA, func(t A) string {
return t.ID
})
fmt.Println(result)
}
随手写的 大概是这个意思吧
- struct (marshal) -> json
- 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
}
func convertSliceToMap(slice []MyStruct) map[int]MyStruct {
result := make(map[int]MyStruct)
for _, item := range slice {
result[item.id] = item
}
return result
}
func ToMap2(d []interface{}, getID func(interface{}) interface{}) map[interface{}]interface{} {
mp := make(map[interface{}]interface{}, len(d))
for _, o := range d {
mp[getID(o)] = o
}
return mp
}
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 的方法,然后就能调用函数一行转了。
// Mappable can be converted to a Map, and it is usually a struct with a unique primary key field.
// Please implement a method named UniqueKey() that returns a key that can be used in Map.
type Mappable interface {
UniqueKey() any
}
// ConvertToMap can convert an array of struct to map
func ConvertToMap[E Mappable](s []E) map[any]E {
m := make(map[any]E)
for i := range s {
m[s[i].UniqueKey()] = s[i]
}
return m
}
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
func structToMap(obj interface{}) (map[string]interface{}, error) {
objMap := make(map[string]interface{})
s, err := json.Marshal(&obj)
if err != nil {
return nil, err
}
err = json.Unmarshal(s, &objMap)
if err != nil {
return nil, err
}
return objMap, nil
}
func slice2map(input []S) map[int]S {
这不是随便写吗?
}
不知道你要的通用是啥意思:
func slice2mapT any map[int]T {
泛型试试?
}
slice 里的 index 和元素的 ID 一定对应吗?不对应怎么处理?
// You can edit this code!
// Click here and start typing.
package main
import "fmt"
type S struct {
ID int
Name string
}
func (s S) GetID() int {
return s.ID
}
type IS interface {
S
GetID() int
}
func slice2map[T IS](in []T) map[int]T {
out := map[int]T{}
for _, s := range in {
out[s.GetID()] = s
}
return out
}
func main() {
input := []S{{1, "a"}, {2, "b"}}
fmt.Println(slice2map(input))
}
github.com/samber/lo 请
有谁能试一下,让 chatgpt 做这事
你的这个需求前面那几个简单的就可以了,如果要做复杂的映射,我一般这么写
如果你写过类似函数式的东西应该知道啥意思
func SliceToMap[U comparable, T, G any](s []T, keySelector func(int, T) U, valueMapper func(int, T) G) map[U]G {
m := make(map[U]G)
for i, v := range s {
m[keySelector(i, v)] = valueMapper(i, v)
}
return m
}
func Usage() {
type named struct {
Name string
Value string
}
type additional struct {
Age int
Name string
Value string
}
s := []named{{"1", "1"}, {"2", "2"}, {"3", "3"}}
targetMap := SliceToMap(s,
func(idx int, v named) string { return v.Name },
func(idx int, v named) additional { return additional{idx, v.Name, v.Value} })
}
假设你有一个结构体类型为 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() 方法
我在商场里看了几家体验店,小米 vivoOPPO 三星,只有 vivo 的热点共享里不包含以太网网络共享这个功能,其他几家都有,无论是低端机还是旗舰机。 这个 USB 共享?…
XML可能是计算有史以来最NB的发明了,以至于我们以没有XML的程序是难登大堂的程序,不用XML,你都不好意思当程序员。于是,我们看到了很多很雷人的用法(《信XML,得永生》)…
写了一个虚拟币的价格监控程序,有异动的时候就邮箱提醒,发件地址是 [email&#160;protected] 。 晚上刚开始跑的时候正常,发了信就能接收到通知,设置的发件人姓…