go 如何写一个通用的方法,将一个结构体切片,根据 id 字段,转为 map 结构 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
craftx
V2EX    Go 编程语言

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

  •  
  •   craftx 2023-02-20 09:01:27 +08:00 3696 次点击
    这是一个创建于 971 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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", }, } 

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

    第 1 条附言    2023-02-20 15:23:14 +08:00
    21 条回复    2023-02-21 16:31:39 +08:00
    wueryi
        1
    wueryi  
       2023-02-20 09:05:25 +08:00
    刚发现别的帖子哈哈哈哈 现学现卖
    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
    }
    wangsongyan
        2
    wangsongyan  
       2023-02-20 09:08:56 +08:00
    遍历 slice 转为 map ,取出 ID 放新 map
    sqfphoenix
        3
    sqfphoenix  
       2023-02-20 09:21:09 +08:00
    ```
    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)
    }
    ```

    随手写的 大概是这个意思吧
    jorneyr
        4
    jorneyr  
       2023-02-20 09:23:01 +08:00   1
    1. struct (marshal) -> json
    2. json (unmarshal)-> map
    Nazz
        5
    Nazz  
       2023-02-20 09:23:46 +08:00 via Android
    需要泛型
    yaott2020
        6
    yaott2020  
       2023-02-20 09:30:02 +08:00 via Android
    反射?
    Nazz
        7
    Nazz  
       2023-02-20 09:31:55 +08:00
    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
    }
    sarices
        8
    sarices  
       2023-02-20 09:35:46 +08:00
    ```
    func convertSliceToMap(slice []MyStruct) map[int]MyStruct {
    result := make(map[int]MyStruct)
    for _, item := range slice {
    result[item.id] = item
    }
    return result
    }
    ```
    sparklee
        9
    sparklee  
       2023-02-20 09:50:05 +08:00
    ```

    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
    }

    ```
    chitanda
        10
    chitanda  
       2023-02-20 10:02:43 +08:00
    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()
    }
    Muninn
        11
    Muninn  
       2023-02-20 10:10:41 +08:00
    我尝试把 KV 都用泛型,发现它无法自动推导,使用起来很不方便。后来把 key 写成 any 。

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

    ```go
    // 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
    }
    ```
    flywheel
        12
    flywheel  
       2023-02-20 10:19:09 +08:00
    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
    }
    cexll
        13
    cexll  
       2023-02-20 11:49:24 +08:00
    问了一下 ChatGPT
    ```go
    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

    }
    ```
    rrfeng
        14
    rrfeng  
       2023-02-20 12:23:31 +08:00
    func slice2map(input []S) map[int]S {
    这不是随便写吗?
    }

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

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

    slice 里的 index 和元素的 ID 一定对应吗?不对应怎么处理?
    rrfeng
        15
    rrfeng  
       2023-02-20 12:30:28 +08:00
    ```
    // 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))
    }

    ```
    jitongxi
        16
    jitongxi  
       2023-02-20 12:46:01 +08:00   1
    listenfree
        17
    listenfree  
       2023-02-20 12:51:12 +08:00
    有谁能试一下,让 chatgpt 做这事
    pkoukk
        18
    pkoukk  
       2023-02-20 13:59:49 +08:00
    你的这个需求前面那几个简单的就可以了,如果要做复杂的映射,我一般这么写
    如果你写过类似函数式的东西应该知道啥意思
    ```go
    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} })

    }
    ```
    kuqma98
        19
    kuqma98  
       2023-02-20 14:33:31 +08:00
    @listenfree
    假设你有一个结构体类型为 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 ,你也可以直接编写一个特定类型的方法,而不是使用反射。
    yrj
        20
    yrj  
       2023-02-20 21:04:28 +08:00
    哈哈,这题我会,因为我之前写过,用的反射把 ID 取出来。如果用泛型的话,好像不能取出 ID 属性吧。除非有 ID() 方法
    thinkingbullet
        21
    thinkingbullet  
       2023-02-21 16:31:39 +08:00
    @kuqma98 panic: interface conversion: interface {} is map[int]main.Person, not map[int]interface {}, 这里出问题了:mapValue.Interface().(map[int]interface{})
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     892 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 22:17 PVG 06:17 LAX 15:17 JFK 18:17
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86