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 · 3260 次点击
    这是一个创建于 648 天前的主题,其中的信息可能已经有所发展或是发生改变。

    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
    刚发现别的帖子哈哈哈哈 现学现卖
    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
    } 😝 😝 😝
    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   ·   实用小工具   ·   1012 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:36 · PVG 03:36 · LAX 11:36 · JFK 14:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.