如何在忽略空字符串的情况下连接结构的字符串字段?

I am really new to Go, so want some advice. I have a struct:

type Employee struct {
    Name        string
    Designation string
    Department  string
    Salary      int
    Email       string
}

I want to concatenate the string fields into a type of employee description. So that, I can say: toString(employee) and get:

John Smith Manager Sales john.smith@example.com

I tried to fetch each field, check if they are empty and put them in a slice and join them at the end

employeeDescArr := make([]string, 0, 4)
if strings.TrimSpace(value) != "" {
    append(employee.GetName(), value)
}...
return strings.Join(employeeDescArr[:], " ")

I think this method is very verbose and shows lack of Go skills. Is it better to use a string Builder instead? Is there a way to iterate through all fields of a struct in a Reflection way and join them?

Loop through the string fields and collect non-empty strings. Join the fields.

func (e *Employee) String() string {
    var parts []string
    for _, s := range []string{e.Name, e.Designation, e.Department, e.Email} {
        if strings.TrimSpace(s) != "" {
            parts = append(parts, s)
        }
    }
    return strings.Join(parts, " ")
}

Because the strings.Join function is implemented using strings.Builder, there's no benefit to replacing strings.Join with application code that uses strings.Builder.

Here's how to use reflect to avoid listing the fields in the string function:

var stringType = reflect.TypeOf("")

func (e *Employee) String() string {
    v := reflect.ValueOf(e).Elem()
    var parts []string
    for i := 0; i < v.NumField(); i++ {
        f := v.Field(i)
        if f.Type() == stringType {
            s := f.String()
            if strings.TrimSpace(s) != "" {
                parts = append(parts, s)
            }
        }
    }
    return strings.Join(parts, " ")
}

If you want to include all fields (include non-strings and empty strings), then you can fmt.Sprint(e) to get a string. See https://play.golang.org/p/yntZxQ-Xs6C.

You can make it less verbose by writing an utility function to make the addition with the "non-blank-string" check.

Also, you could make your type implement implement an String() method which has the advantage it will make it print as you wish when used in fmt print functions.

This addToString function is generic so you could reuse it if doing this for other types:

func addToString(original string, addition interface{}) string {
    additionStr := fmt.Sprint(addition)
    if additionStr != "" {
        if original != "" {
            original += " "
        }
        original += additionStr
    }
    return original
}

Then you can implement it like this, which is not so verbose:

type Employee struct {
    Name        string
    Designation string
    Department  string
    Salary      int
    Email       string
}

func (e *Employee) String() string {
    theString := ""
    theString = addToString(theString, e.Name)
    theString = addToString(theString, e.Designation)
    theString = addToString(theString, e.Department)
    theString = addToString(theString, e.Salary)
    theString = addToString(theString, e.Email)
    return theString
}

And use it like this:

func main() {
    emp := &Employee{
        Name: "Jonh",
        Department: "Some dept",
    }
    fmt.Println(emp.String())
    fmt.Println(emp)
}

Which will output:

Jonh Some dept 0
Jonh Some dept 0

I think you would want to use the Stringer interface instead. ie:

package main

import (
  "fmt"
  "strings"
  "strconv"
)
type Employee struct {
    Name        string
    Designation string
    Department  string
    Salary      int
    Email       string
}
func main() {
  emp1:=Employee{Name:"Cetin", Department:"MS", Salary:50}
  emp2:=Employee{Name:"David", Designation:"Designation", Email:"david@nowhere.com"}
  emp3:=Employee{Department:"Space", Salary:10}

  fmt.Println(emp1)
  fmt.Println(emp2)
  fmt.Println(emp3)

}

func (e Employee) String() string {
    var salary string
    if e.Salary > 0 { 
      salary = strconv.Itoa(e.Salary) + " " 
    } else {
      salary = ""
    }
    return strings.TrimSpace(
        strings.TrimSpace(
        strings.TrimSpace(e.Name + " " + e.Designation) + " " +
        e.Department) + " " +
    salary +
        e.Email)
}

Playground: https://play.golang.org/p/L8ft7SeXpqt

PS: I later noticed you only want string fields, but didn't remove salary anyway.

Package fmt

import "fmt" 

type Stringer

Stringer is implemented by any value that has a String method, which defines the “native” format for that value. The String method is used to print values passed as an operand to any format that accepts a string or to an unformatted printer such as Print.

type Stringer interface {
        String() string
}

Write a String method for type Employee.

For example,

package main

import (
    "fmt"
    "strings"
)

type Employee struct {
    Name        string
    Designation string
    Department  string
    Salary      int
    Email       string
}

func appendItem(items *strings.Builder, item string) {
    if len(item) > 0 {
        if items.Len() > 0 {
            items.WriteByte(' ')
        }
        items.WriteString(item)
    }
}

func (e Employee) String() string {
    s := new(strings.Builder)
    appendItem(s, e.Name)
    appendItem(s, e.Designation)
    appendItem(s, e.Department)
    appendItem(s, e.Email)
    return s.String()
}

func main() {
    ee := Employee{
        Name:        "John Smith",
        Designation: "Manager",
        Department:  "Sales",
        Email:       "john.smith@example.com",
        Salary:      42000,
    }
    fmt.Println(ee)
}

Playground: https://play.golang.org/p/EPBjgi8usJ-

Output:

John Smith Manager Sales john.smith@example.com

I want to concatenate the string fields into a type of employee description.

Is there a way to iterate through all fields of a struct in a Reflection way and join them?

[Reflection is] a powerful tool that should be used with care and avoided unless strictly necessary. Rob Pike

The Go Blog: The Laws of Reflection

Reflection is never clear. Rob Pike

Go Proverbs - Rob Pike - Gopherfest - November 18, 2015

Go compiled code is efficient. The Go reflect package functions are interpreted at run time.

Iterating through all fields of a struct has the same bug as SELECT * FROM table; in SQL. The values returned are determined at run time, not compile time.

If your case, The business requirement is to hide confidential fields, like salary, and limit the fields displayed to a few key descriptive fields. Inevitably, fields will be added to the struct. The "concatenate the string fields" specification is unlikely to be correct now or in the future.