英文:
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
包含两个字段 B
和 A
。通过创建结构体实例 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论