一尘不染

如何按字母顺序对结构字段排序

go

如何获得按字段排序的struct输出?

type T struct {
    B int
    A int
}

t := &T{B: 2, A: 1}

doSomething(t)

fmt.Println(t)  // &{1 2} --> Sorted by fields

阅读 242

收藏
2020-07-02

共1个答案

一尘不染

A struct是字段的 有序
集合。该fmt包使用反射来获取值的字段和值struct,并按照定义它们的顺序生成输出。

因此,最简单的解决方案是在已经按字母顺序排列字段的位置声明类型:

type T struct {
    A int
    B int
}

如果您不能修改字段的顺序(例如,内存布局很重要),则可以Stringer通过String()为结构类型指定一个方法来实现接口:

func (t T) String() string {
    return fmt.Sprintf("{%d %d}", t.A, t.B)
}

所述fmt包检查所传递的值工具Stringer,并且如果是的话,调用它的String()方法,以产生输出。

该解决方案的缺点是这种方法不灵活(例如,如果添加新字段,也必须更新String()方法),还必须针对struct希望其工作的每种类型执行此操作(并且无法定义方法)用于其他包中定义的类型)。

完全灵活的解决方案可以使用反射。您可以获取字段名称,按名称对其进行排序,然后遍历排序后的名称并获取字段值(按名称)。

该解决方案的优点是,它适用于任何struct,即使您在结构中添加或删除字段,它也无需修改即可正常工作。它也适用于任何类型的字段,而不仅仅是int字段。

这是一个示例(在Go Playground上尝试):

func printFields(st interface{}) string {
    t := reflect.TypeOf(st)

    names := make([]string, t.NumField())
    for i := range names {
        names[i] = t.Field(i).Name
    }
    sort.Strings(names)

    v := reflect.ValueOf(st)
    buf := &bytes.Buffer{}
    buf.WriteString("{")
    for i, name := range names {
        val := v.FieldByName(name)
        if !val.CanInterface() {
            continue
        }
        if i > 0 {
            buf.WriteString(" ")
        }
        fmt.Fprintf(buf, "%v", val.Interface())
    }
    buf.WriteString("}")

    return buf.String()
}
2020-07-02