英文:
Converting Struct to CSV string
问题
你可以使用反射(reflect)包来遍历结构体的字段,并将字段名作为表头,将字段值作为该表头下的各列,用逗号分隔,构建一个CSV字符串。以下是一个实现的示例代码:
package main
import (
"encoding/csv"
"fmt"
"os"
"reflect"
"strings"
)
type Data struct {
id []string
col1 []float64
col2 []float64
}
func main() {
d := &Data{
id: []string{"id_1", "id_1", "id_1", "id_1"},
col1: []float64{340.384926, 321.385028, 520.341473, 500.385473},
col2: []float64{123.285031, 4087.284675, -8958.284216, -7612.283668},
}
csvString := structToCSV(d)
fmt.Println(csvString)
}
func structToCSV(data interface{}) string {
value := reflect.ValueOf(data)
if value.Kind() != reflect.Ptr || value.IsNil() {
return ""
}
value = value.Elem()
if value.Kind() != reflect.Struct {
return ""
}
var headers []string
var rows [][]string
for i := 0; i < value.NumField(); i++ {
field := value.Field(i)
fieldName := value.Type().Field(i).Name
headers = append(headers, fieldName)
var row []string
for j := 0; j < field.Len(); j++ {
row = append(row, fmt.Sprintf("%v", field.Index(j)))
}
rows = append(rows, row)
}
var csvBuilder strings.Builder
csvWriter := csv.NewWriter(&csvBuilder)
csvWriter.Write(headers)
for _, row := range rows {
csvWriter.Write(row)
}
csvWriter.Flush()
return csvBuilder.String()
}
这段代码会输出以下CSV字符串:
id,col1,col2
id_1,340.384926,123.285031
id_1,321.385028,4087.284675
id_1,520.341473,-8958.284216
id_1,500.385473,-7612.283668
这是通过遍历结构体的字段,将字段名作为表头,将字段值作为各列的值,构建的CSV字符串。你可以根据需要进行修改和扩展。
英文:
I have a struct that gets Scanned in after a DB response like the below. Each field is the same len(). I'd like to take this struct and produce a CSV delimited string/
package main
import "fmt"
type Data struct {
id []string
col1 []float64
col2 []float64
}
func main() {
d := &Data{
id: []string{"id_1", "id_1", "id_1", "id_1"},
col1: []float64{340.384926, 321.385028, 520.341473, 500.385473},
col2: []float64{123.285031, 4087.284675, -8958.284216, -7612.283668},
}
fmt.Printf("%+v", d)
}
I'd like to loop through the struct which I believe is possible using reflect
, then construct a CSV string like the following using the struct field names as the header and t he values as individual columns for that header, delimited by a comma.
`
id,col1,col2
id_1,340.384926,123.285031
id_1,321.385028,4087.284675
id_1,520.341473,-8958.284216
id_1,500.385473,-7612.283668
`
What is an efficient way to achieve this?
答案1
得分: 3
如果可能的话,尽量避免使用reflect
来遍历结构体,因为这可能会导致性能下降和代码可读性降低。不要陷入X-Y问题的陷阱 - 这里的要求是将Data
结构体转换为csv字符串(Y问题),但是X问题是要避免使用Data
等结构体类型作为起点。
许多操作csv的Golang包喜欢使用以下类型:
[][]string
:encoding/csv和struct2csv[]Struct{}
:gocsv和csv2struct
然而,如果无法避免使用Data
类型,你可以先编写一个函数,将Data
转换为[][]string
,同时避免使用reflect
:
func TransformDataTo2DSlice(d Data) [][]string {
numRows := len(d.id)
result := make([][]string, numRows+1)
// 添加表头行
result[0] = []string{"id", "col1", "col2"}
// 添加数据行
for i := 0; i < numRows; i++ {
result[i+1] = []string{d.id[i],
strconv.FormatFloat(d.col1[i], 'f', -1, 64),
strconv.FormatFloat(d.col2[i], 'f', -1, 64),
}
}
return result
}
接下来,使用encoding/csv
中的w.WriteAll()
方法将[][]string
轻松转换为csv:
func main() {
d := Data{
id: []string{"id_1", "id_1", "id_1", "id_1"},
col1: []float64{340.384926, 321.385028, 520.341473, 500.385473},
col2: []float64{123.285031, 4087.284675, -8958.284216, -7612.283668},
}
d2dslice := TransformDataTo2DSlice(d)
// fmt.Printf("%+v", d2dslice)
// [[id, col1, col2],
// [id_1, 340.384926, 123.285031],
// [id_1, 321.385028, 4087.284675],
// [id_1, 520.341473, -8958.284216],
// [id_1,500.385473,-7612.283668]]
w := csv.NewWriter(os.Stdout)
w.WriteAll(d2dslice)
if err := w.Error(); err != nil {
log.Fatalln("error writing csv:", err)
}
// 输出到标准输出:
// id,col1,col2
// id_1,340.384926,123.285031
// id_1,321.385028,4087.284675
// id_1,520.341473,-8958.284216
// id_1,500.385473,-7612.283668
}
在这里运行上述程序:go-playground
如果要将csv写入字符串变量中,可以传入一个缓冲区:
buf := new(bytes.Buffer)
w := csv.NewWriter(buf)
w.WriteAll(d2dslice)
if err := w.Error(); err != nil {
log.Fatalln("error writing csv:", err)
}
csvString := buf.String()
fmt.Printf("%T\n", csvString) // 打印变量类型
// string
fmt.Printf("%+v\n", csvString) // 打印变量值
// id,col1,col2
// id_1,340.384926,123.285031
// id_1,321.385028,4087.284675
// id_1,520.341473,-8958.284216
// id_1,500.385473,-7612.283668
英文:
If possible, avoid using reflect
to iterate through struct because it can result in decreased performance and reduced code readability. Don't fall for the XY problem - the ask here is to transform Data
struct into csv string (Y problem), but the X problem here is avoid using struct type such as Data
as starting point.
Many golang packages that manipulate csv prefer:
-
[][]string
: encoding/csv and struct2csv -
[]Struct{}
: gocsv and csv2struct
However, if Data
type is unavoidable, you can first write a function that transforms Data
into [][]string
while avoiding using reflect
:
func TransformDataTo2DSlice(d Data) [][]string {
numRows := len(d.id)
result := make([][]string, numRows+1)
// Add header row
result[0] = []string{"id", "col1", "col2"}
// Add data rows
for i := 0; i < numRows; i++ {
result[i+1] = []string{d.id[i],
strconv.FormatFloat(d.col1[i], 'f', -1, 64),
strconv.FormatFloat(d.col2[i], 'f', -1, 64),
}
}
return result
}
Next, use w.WriteAll()
method from encoding/csv
to easily transform [][]string
to csv
func main() {
d := Data{
id: []string{"id_1", "id_1", "id_1", "id_1"},
col1: []float64{340.384926, 321.385028, 520.341473, 500.385473},
col2: []float64{123.285031, 4087.284675, -8958.284216, -7612.283668},
}
d2dslice := TransformDataTo2DSlice(d)
// fmt.Printf("%+v", d2dslice)
// [[id, col1, col2],
// [id_1, 340.384926, 123.285031],
// [id_1, 321.385028, 4087.284675],
// [id_1, 520.341473, -8958.284216],
// [id_1,500.385473,-7612.283668]]
w := csv.NewWriter(os.Stdout)
w.WriteAll(d2dslice)
if err := w.Error(); err != nil {
log.Fatalln("error writing csv:", err)
}
// stdout:
// id,col1,col2
// id_1,340.384926,123.285031
// id_1,321.385028,4087.284675
// id_1,520.341473,-8958.284216
// id_1,500.385473,-7612.283668
}
Run the program above here: go-playground
To write csv to a string variable instead, pass in a buffer:
buf := new(bytes.Buffer)
w := csv.NewWriter(buf)
w.WriteAll(d2dslice)
if err := w.Error(); err != nil {
log.Fatalln("error writing csv:", err)
}
csvString := buf.String()
fmt.Printf("%T\n", csvString) // print the variable type
// string
fmt.Printf("%+v\n", csvString) // print the variable value
// id,col1,col2
// id_1,340.384926,123.285031
// id_1,321.385028,4087.284675
// id_1,520.341473,-8958.284216
// id_1,500.385473,-7612.283668
答案2
得分: 2
使用Golang的encoding/csv
包。
https://pkg.go.dev/encoding/csv.
答案3
得分: 0
- 步骤1:将数据结构转换为二维数组
[][]string
- 步骤2:打印二维数组的结果。
package main
import (
"fmt"
"log"
"reflect"
)
type Data struct {
Id []string `json:"id"`
Col1 []float64 `json:"col1"`
Col2 []float64 `json:"col2"`
}
func (d *Data) String() string {
dataType := reflect.TypeOf(*d)
dataValue := reflect.ValueOf(*d)
// 步骤1:将数据结构转换为二维数组。
var result [][]string // [列][每列的行数]
for col := 0; col < dataType.NumField(); col++ {
filedType := dataType.Field(col)
filedValue := dataValue.Field(col)
if result == nil {
colMax := dataType.NumField()
result = make([][]string, colMax)
}
var rowMax int
switch filedValue.Interface().(type) {
case []string:
rowMax = len(filedValue.Interface().([]string))
case []float64:
rowMax = len(filedValue.Interface().([]float64))
default:
log.Fatalf("不支持的类型。%v", filedValue.Interface())
}
if result[col] == nil {
result[col] = make([]string, rowMax+1) // 1 用于表头
}
// 表头
result[col][0] = fmt.Sprintf("%v", filedType.Tag.Get("json"))
// 数据
var cell interface{}
for row := 1; row <= rowMax; row++ {
switch filedValue.Interface().(type) {
case []string:
cell = filedValue.Interface().([]string)[row-1]
case []float64:
cell = filedValue.Interface().([]float64)[row-1]
default:
log.Fatalf("不支持的类型。%v", filedValue.Interface())
}
result[col][row] = fmt.Sprintf("%v", cell)
}
}
// 步骤2:打印二维数组的结果。
str := ""
const sep = ","
for row := 0; row < len(result[0]); row++ {
for col := 0; col < len(result); col++ {
str += fmt.Sprintf("%v", result[col][row])
if col < len(result)-1 {
str += sep
}
}
if row < len(result[0])-1 {
str += "\n"
}
}
return str
}
func main() {
d := &Data{
Id: []string{"id_1", "id_1", "id_1", "id_1"},
Col1: []float64{340.384926, 321.385028, 520.341473, 500.385473},
Col2: []float64{123.285031, 4087.284675, -8958.284216, -7612.283668},
}
fmt.Println(d)
}
输出:
id,col1,col2
id_1,340.384926,123.285031
id_1,321.385028,4087.284675
id_1,520.341473,-8958.284216
id_1,500.385473,-7612.283668
<kbd>go-playground</kbd>
英文:
- Step 1: Convert the data structure into a 2-dimensional array
[][]string
- Step 2: Print the result of the 2-dimensional array.
package main
import (
"fmt"
"log"
"reflect"
)
type Data struct {
Id []string `json:"id"`
Col1 []float64 `json:"col1"`
Col2 []float64 `json:"col2"`
}
func (d *Data) String() string {
dataType := reflect.TypeOf(*d)
dataValue := reflect.ValueOf(*d)
// Step 1: Convert the data structure into a 2-dimensional array.
var result [][]string // [col][how many 'row' on each col]
for col := 0; col < dataType.NumField(); col++ {
filedType := dataType.Field(col)
filedValue := dataValue.Field(col)
if result == nil {
colMax := dataType.NumField()
result = make([][]string, colMax)
}
var rowMax int
switch filedValue.Interface().(type) {
case []string:
rowMax = len(filedValue.Interface().([]string))
case []float64:
rowMax = len(filedValue.Interface().([]float64))
default:
log.Fatalf("not support type. %v", filedValue.Interface())
}
if result[col] == nil {
result[col] = make([]string, rowMax+1) // 1 for header
}
// header
result[col][0] = fmt.Sprintf("%v", filedType.Tag.Get("json"))
// data
var cell any
for row := 1; row <= rowMax; row++ {
switch filedValue.Interface().(type) {
case []string:
cell = filedValue.Interface().([]string)[row-1]
case []float64:
cell = filedValue.Interface().([]float64)[row-1]
default:
log.Fatalf("not support type. %v", filedValue.Interface())
}
result[col][row] = fmt.Sprintf("%v", cell)
}
}
// Step 2: Print the result of the 2-dimensional array.
str := ""
const sep = ","
for row := 0; row < len(result[0]); row++ {
for col := 0; col < len(result); col++ {
str += fmt.Sprintf("%v", result[col][row])
if col < len(result)-1 {
str += sep
}
}
if row < len(result[0])-1 {
str += "\n"
}
}
return str
}
func main() {
d := &Data{
Id: []string{"id_1", "id_1", "id_1", "id_1"},
Col1: []float64{340.384926, 321.385028, 520.341473, 500.385473},
Col2: []float64{123.285031, 4087.284675, -8958.284216, -7612.283668},
}
fmt.Println(d)
}
output:
id,col1,col2
id_1,340.384926,123.285031
id_1,321.385028,4087.284675
id_1,520.341473,-8958.284216
id_1,500.385473,-7612.283668
<kbd>go-playground</kbd>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论