Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to sort struct fields in alphabetical order

Tags:

sorting

struct

go

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
like image 406
tomodian Avatar asked Sep 17 '25 08:09

tomodian


1 Answers

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()
}
like image 51
icza Avatar answered Sep 19 '25 05:09

icza