将自定义类型的Slice转换为字符串的slice

时间:2019-11-22 06:29:35

标签: go

我有多种结构,例如波纹管

type Person struct {
    first string
    last  string
    age   int
}

type Product struct {
    ProductID     int                
    Name          string             
    Description   string             
    Price         float64           
}

我想要一个函数,该函数将任何类型的切片作为第一个参数,并将函数作为第二个参数,它将用于与切片的每个元素一起调用以构造字符串切片并返回该字符串切片。类似于Typescript / scala中的map()或C#中的select()之类的东西。

5 个答案:

答案 0 :(得分:2)

由于Go没有泛型,因此“任何类型”只能表示interface{}。您可以输入以下内容:

func ToStringSlice(arr []interface{}, convert func(interface{}) string) []string {
    ret := []string{}
    for _, elem := range arr {
        ret = append(ret, convert(elem))
    }
    return ret
}

然后,您基本上可以注入您想要的任何转换函数。例如

fmt.Println(ToStringSlice([]interface{}{1, 2, "foo", "bar"},
    func(x interface{}) string {
        return fmt.Sprint(x)
    }))

由于字符串转换可能会变糟,因此我还要添加错误检查:

// Define the function with an error return.
type Stringifier func(interface{}) (string, error)

func ToStringSlice(arr []interface{}, convert Stringifier) ([]string, error) {
    ret := []string{}
    for _, elem := range arr {
        if s, e := convert(elem); e != nil {
           return nil, e
        } else {
           ret = append(ret, s)
        }

    }
    return ret, nil
}

答案 1 :(得分:1)

这是最适合我的解决方案

type Person struct {
    first string
    last  string
    age   int
}

type Product struct {
    ProductID     int                
    Name          string             
    Description   string             
    Price         float64           
}

func mapToStringSlice(slice interface{}, mapFunc func(interface{}) string) []string {
    s := reflect.ValueOf(slice)
    if s.Kind() != reflect.Slice {
        panic("mapToStringSlice() given a non-slice type")
    }
    ret := make([]string, s.Len())

    for i:=0; i<s.Len(); i++ {
        ret[i] = mapFunc(s.Index(i).Interface())
    }

    return ret
}


func main() {
persons := []Person{{
        first: "A",
        last:  "g",
        age:   20,
    },{
        first: "B",
        last:  "r",
        age:   40,
    },{
        first: "C",
        last:  "",
        age:   0,
    }}

products := []Product{Product{ProductID: 1}, Product{ProductID: 2}}

    personFirstNames := mapToStringSlice(persons, func(input interface{}) string {
        return input.(Person).first
    })
    productIDs := mapToStringSlice(products, func(input interface{}) string {
        return input.(Product).ProductID
    })

}

答案 2 :(得分:0)

一个解决方案涉及fmt.Stringer

package main

import (
    "fmt"
)

func main() {

    items := []fmt.Stringer{Product{ProductID: 1}, Person{first: "first"}}

    var res []string
    for _, i := range items {
        res = append(res, i.String())
    }
    fmt.Println(res)

}

type Person struct {
    first string
    last  string
    age   int
}

func (p Person) String() string { return p.first }

type Product struct {
    ProductID   int
    Name        string
    Description string
    Price       float64
}

func (p Product) String() string { return fmt.Sprint(p.ProductID) }

答案 3 :(得分:0)

另一种解决方案涉及反射包

package main

import (
    "fmt"
    "reflect"
)

func main() {

    products := []Product{Product{ProductID: 1}, Product{ProductID: 2}}
    persons := []Person{Person{first: "first"}, Person{first: "second"}}

    fmt.Println(tostrslice(products))
    fmt.Println(tostrslice(persons))

}

func tostrslice(a interface{}) (ret []string) {

    rv := reflect.ValueOf(a)

    if rv.Kind() != reflect.Slice {
        return
    }

    for i := 0; i < rv.Len(); i++ {
        if rv.Index(i).NumField() > 0 {
            f := rv.Index(i).FieldByIndex([]int{0})
            if f.CanInterface() {
                v := f.Interface()
                ret = append(ret, fmt.Sprint(v))
            }
        }
    }
    return
}

type Person struct {
    first string
    last  string
    age   int
}


type Product struct {
    ProductID   int
    Name        string
    Description string
    Price       float64
}

golang提供了许多方法来满足您的需求,它们各有利弊,在使用时应注意您追求的目标。

答案 4 :(得分:0)

这是interface的典型用例。幸运的是,您需要的接口已经以fmt.Stringerhttps://golang.org/pkg/fmt/#Stringer)的形式存在:

看看https://play.golang.org/p/d1sNPLKhNCU ...

首先,您的结构是实现Stringer接口所必需的。这可以通过简单地添加一个String()来完成,例如:

type Product struct {
    ProductID   int
    Name        string
    Description string
    Price       float64
}

func (p Product) String() string {
    return fmt.Sprintf("%d %s", p.ProductID, p.Name)
}

现在您的Product也是Stringer,因此可以在接受Stringers的任何函数中传递:

func AsStringSlice(in []fmt.Stringer) []string {
    out := []string{}
    for _, i := range in {
        out = append(out, i.String())
    }
    return out
}

由于“ interface {}什么也没说”(https://go-proverbs.github.io/),我建议尽可能少使用interface {}。这是通过这种方法实现的。同样,您可以避免这种反射。

另一方面,您必须能够管理结构,例如。实现String函数。如果由于类型来自依赖项而无法实现,请考虑将它们包装起来:

type MyTypeWithStringer package.ForeinTypeWithoutStringer

func(t MyTypeWithStringer) String() string {
    // ...
}