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

huangapple go评论89阅读模式
英文:

How to sort struct fields in alphabetical order

问题

如何按字段排序以获得结构体的输出?

type T struct {
    B int
    A int
}

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

doSomething(t)

fmt.Println(t)  // &{1 2} --> 按字段排序

以上代码中,结构体 T 包含两个字段 BA。通过创建结构体实例 t 并设置字段的值,然后调用 doSomething 函数对结构体进行操作。最后,使用 fmt.Println 打印结构体 t 的值,输出结果为 &{1 2},表示按字段排序后的结果。

英文:

How could I get an output of struct, sorted by fields?

type T struct {
    B int
    A int
}

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

doSomething(t)

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

答案1

得分: 10

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()
}
英文:

A struct is an ordered collection of fields. The fmt package uses reflection to get the fields and values of a struct value, and generates output in the order in which they were defined.

So the simplest solution would be to declare your type where you already have your fields arranged in alphabetical order:

type T struct {
    A int
    B int
}

If you can't modify the order of fields (e.g. memory layout is important), you can implement the Stringer interface by specifying a String() method for your struct type:

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

The fmt package checks if the passed value implements Stringer, and if it does, calls its String() method to generate the output.

Cons of this solution is that this is not flexible (e.g. if you add a new field, you have to update the String() method too), also you have to do it for every struct type you want it to work (and you can't define methods for types defined in other packages).

The completely flexible solution can use reflection. You can get the names of fields, sort them by name, and then iterate over the sorted names and get the field values (by name).

Pros of this solution is that this works for any struct, and it keeps working without modification even if you add or remove fields from your structs. It also works for fields of any type, not just for int fields.

Here is an example how to do it (try it on the 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()
}

答案2

得分: 1

T 实现 Stringer 接口(参见 fmt 包),并首先打印 A 或 B。

顺便说一句,这是个愚蠢的主意。

英文:

Make T implement the Stringer interface (see package fmt) and do either print A orb B first.

BTW. This is a stupid idea.

huangapple
  • 本文由 发表于 2015年6月18日 18:58:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/30913483.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定