-1

我有一个 Deduplicate 方法,它返回作为 interface{} 传入的 slice 的去重副本。有没有办法将此方法 interface{} 返回的值转换为与我在此方法中传递的相同类型而不显式编写它?例如,如果我将 myStruct.RelatedIDs 类型从 []int 更改为 []uint,它将阻止代码编译。

https://play.golang.org/p/8OT4xYZuwEn

package main

import (
    "fmt"
    "reflect"
)

type myStruct struct {
    ID         int
    RelatedIDs []int
}

func main() {
    s := &myStruct{
        ID:         42,
        RelatedIDs: []int{1, 1, 2, 3},
    }
    v, _ := Deduplicate(s.RelatedIDs)
    s.RelatedIDs = v.([]int) // << can I assert type dynamically here?
    // s.RelatedIDs = v.(reflect.TypeOf(s.RelatedIDs)) // does not work
    fmt.Printf("%#v\n", s.RelatedIDs)
}

func Deduplicate(slice interface{}) (interface{}, error) {
    if reflect.TypeOf(slice).Kind() != reflect.Slice {
        return nil, fmt.Errorf("slice has wrong type: %T", slice)
    }

    s := reflect.ValueOf(slice)
    res := reflect.MakeSlice(s.Type(), 0, s.Len())

    seen := make(map[interface{}]struct{})
    for i := 0; i < s.Len(); i++ {
        v := s.Index(i)
        if _, ok := seen[v.Interface()]; ok {
            continue
        }
        seen[v.Interface()] = struct{}{}
        res = reflect.Append(res, v)
    }

    return res.Interface(), nil
}

4

2 回答 2

3

为了完整起见,这里是一个通用版本(Playground),它不需要反射。仅在2022 年 2 月开放!不过,关于 apply的常见警告。NaN

package main

import (
    "fmt"
)

func main() {
    s := []int{1, 1, 2, 3}
    res := Deduplicate(s)
    fmt.Printf("%#v\n", res)
}

func Deduplicate[T comparable](s []T) []T {
    seen := make(map[T]struct{})
    res := make([]T, 0, len(s))
    for _, elem := range s {
        if _, exists := seen[elem]; exists {
            continue
        }
        seen[elem] = struct{}{}
        res = append(res, elem)
    }
    return res
}

输出:

[]int{1, 2, 3}
于 2021-07-23T20:03:46.827 回答
3

尝试这个

package main

import (
    "fmt"
    "reflect"
)

type myStruct struct {
    ID         int
    RelatedIDs []int
}

func main() {
    s := &myStruct{
        ID:         42,
        RelatedIDs: []int{1, 1, 2, 3},
    }
    err := Deduplicate(&s.RelatedIDs)
    fmt.Println(err)
    // s.RelatedIDs = v.([]int) // << can I assert type dynamically here?
    // s.RelatedIDs = v.(reflect.TypeOf(s.RelatedIDs)) // does not work
    fmt.Printf("%#v\n", s.RelatedIDs)
}

func Deduplicate(slice interface{}) error {
    rts := reflect.TypeOf(slice)
    rtse := rts.Elem()
    if rts.Kind() != reflect.Ptr && rtse.Kind() != reflect.Slice {
        return fmt.Errorf("slice has wrong type: %T", slice)
    }

    rvs := reflect.ValueOf(slice)
    rvse := rvs.Elem()

    seen := make(map[interface{}]struct{})
    var e int
    for i := 0; i < rvse.Len(); i++ {
        v := rvse.Index(i)
        if _, ok := seen[v.Interface()]; ok {
            continue
        }
        seen[v.Interface()] = struct{}{}
        rvse.Index(e).Set(v)
        e++
    }

    rvse.SetLen(e)
    rvs.Elem().Set(rvse)

    return nil
}

https://play.golang.org/p/hkEW4u1aGUIi

对于未来的泛型,它可能看起来像这样https://go2goplay.golang.org/p/jobI5wKR8fU

于 2021-07-23T19:17:36.887 回答