3

I have a type with three fields

type Person struct {
    FirstName string
    LastName  string
    Age       int
}

Creating an instance and using default fmt.Sprint() returns {John Smith 45}. However for my use case I need a string of format John, Smith, 45. A comma delimited list without being surrounded by curly braces. Is there a more reusable and effective way than:

fmt.Sprintf("%s, %s, %d", x.FirstName, x.LastName, x.Age)

I will be using this method alot with other types and I would prefer a generic method rather than having to type out a format for each type I use:

func asFields(data interface{}) string {
    // TODO logic here
}
Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Ben
  • 3,160
  • 3
  • 17
  • 34

2 Answers2

7

That exact format is not supported by verbs of the fmt package.

The closest would be

s := fmt.Sprintf("%#v", p)

Which generates a string like:

main.Person{FirstName:"John", LastName:"Smith", Age:45}

If you exactly need what you posted in the question, you may use reflection to iterate over the fields and build the result like this:

func asFields(data interface{}) string {
    v := reflect.ValueOf(data)
    b := &strings.Builder{}
    for i := 0; i < v.NumField(); i++ {
        if i > 0 {
            b.WriteString(", ")
        }
        b.WriteString(fmt.Sprint(v.Field(i).Interface()))
    }
    return b.String()
}

This indeed gives:

John, Smith, 45

Try the examples on the Go Playground.

Note that this asFields() function handles all struct types of course not just your Person. Adjustment would be needed to handle pointers and struct of structs of course.

Also note that alternatively to fmt.Sprint() you may also use fmt.Fprint() directed to the buffer in which we're assembling the string:

func asFields(data interface{}) string {
    v := reflect.ValueOf(data)
    b := &strings.Builder{}
    for i := 0; i < v.NumField(); i++ {
        if i > 0 {
            b.WriteString(", ")
        }
        fmt.Fprint(b, v.Field(i).Interface())
    }
    return b.String()
}

Which of course gives the same result (and may or may not be faster, benchmark it). Try it on the Go Playground.

icza
  • 389,944
  • 63
  • 907
  • 827
  • Also note that it incurs the cost of reflection and will be slower than the static solution that OP is already using. – Adrian Dec 09 '19 at 15:02
2

Can we use this approach, it will be applicable only to Person type struct though ?

https://play.golang.org/p/YI2Nu0q51ls

package main

import (
    "fmt"
)

type Person struct {
    FirstName string
    LastName  string
    Age       int
}

func (p Person) String() string {
    return fmt.Sprintf("%s, %s, %d", p.FirstName, p.LastName, p.Age)
}

func main() {
    p := Person{FirstName: "John",LastName: "Doe", Age: 25}
    fmt.Printf("%v", p)
}

Output:

John, Doe, 25

Similar SO question: ToString() function in Go

Ankit Deshpande
  • 3,476
  • 1
  • 29
  • 42