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() 方法
本来想定 iPhone 16pro 的, 但是看看账户余额和实际手机,还想再憋一年。但是 xr 确实也太卡(内存小), 而且不知道怎么回事,4G 信号越来越差??想着 10 月…
喜欢一个人去吃饭,一个人抽烟,一个人吃完饭转转。 久而久之,就成了无法融入团队的特例了。 但是强行融入,没有共同话题,感觉就是“陪笑”。 问就是有。 应该挺多的? 问就是…
寻求一个高速低成本的公有云下云数据迁移方案 最好是各位彦祖实操过的,有数据支撑 主要关注迁移速度和所需时间 数据量为 10TB 级别,均为文件存储 我简单咨询过云厂商,貌似不能…