如何连接忽略空字符串的结构的字符串字段?

How to concat the string fields of a struct ignoring the empty strings?

我是 Go 的新手,所以需要一些建议。 我有一个结构:

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

我想将字符串字段连接成一种员工描述。 所以,我可以说: toString(employee) 并得到:

John Smith Manager Sales john.smith@example.com

我尝试获取每个字段,检查它们是否为空并将它们放入一个切片并在最后加入它们

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

我认为这种方法非常冗长并且缺乏围棋技巧。 改用字符串生成器更好吗? 有没有办法以反射的方式遍历结构的所有字段并将它们连接起来?

您可以通过编写一个实用函数来使 "non-blank-string" 检查加法变得不那么冗长。

此外,您可以让您的类型实现实现一个 String() 方法,该方法的优点是在 fmt 打印函数中使用时可以按您希望的方式打印。

addToString 函数是通用的,因此如果对其他类型执行此操作,您可以重用它:

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

那么你可以这样实现,不那么冗长:

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
}

并像这样使用它:

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

将输出:

Jonh Some dept 0
Jonh Some dept 0

我认为您会希望改用 Stringer 接口。即:

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)
}

游乐场:https://play.golang.org/p/L8ft7SeXpqt

PS:我后来注意到你只想要字符串字段,但并没有删除工资。

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
}

为类型Employee写一个String方法。

例如,

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)
}

游乐场:https://play.golang.org/p/EPBjgi8usJ-

输出:

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?

[反射是]一个强大的工具,除非绝对必要,否则应谨慎使用并避免使用。罗伯·派克

The Go Blog: The Laws of Reflection

倒影永远不清晰。罗伯·派克

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

Go 编译的代码是高效的。 Go 反射包函数在 运行 时间被解释。

迭代结构的所有字段与 SQL 中的 SELECT * FROM table; 存在相同的错误。返回的值是在 运行 时确定的,而不是编译时。

如果你的情况,业务需求是隐藏机密字段,如工资,并将显示的字段限制为几个关键的描述性字段。不可避免地,字段将被添加到结构中。 "concatenate the string fields" 规范现在或将来不太可能正确。

遍历字符串字段并收集 non-empty 个字符串。加入字段。

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, " ")
}

因为 strings.Join 函数是使用 strings.Builder 实现的,所以将 strings.Join 替换为使用 strings.Builder.

的应用程序代码没有任何好处

以下是如何使用反射来避免在字符串函数中列出字段:

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, " ")
}

如果你想包含所有字段(包含non-strings和空字符串),那么你可以fmt.Sprint(e)得到一个字符串。参见 https://play.golang.org/p/yntZxQ-Xs6C