diff --git a/README.md b/README.md index 951c1cc..7ddaa45 100644 --- a/README.md +++ b/README.md @@ -706,6 +706,8 @@ import "github.com/duke-git/lancet/v2/fileutil" [[play](https://go.dev/play/p/OExTkhGEd3_u)] - **WriteCsvFile** : write content to target csv file. [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteCsvFile)] +- **WriteMapsToCsv** : write slice of map to csv file. + [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteMapsToCsv)] [[play](https://go.dev/play/p/dAXm58Q5U1o)] - **WriteBytesToFile** : write bytes to target file. [[doc](https://github.com/duke-git/lancet/blob/main/docs/en/api/packages/fileutil.md#WriteBytesToFile)] diff --git a/README_zh-CN.md b/README_zh-CN.md index 97fe71e..51c574e 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -705,8 +705,10 @@ import "github.com/duke-git/lancet/v2/fileutil" - **ReadCsvFile** : 读取 csv 文件内容到切片。 [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#ReadCsvFile)] [[play](https://go.dev/play/p/OExTkhGEd3_u)] -- **WriteCsvFile** : 向 csv 文件写入内容。 +- **WriteCsvFile** : 向csv文件写入切片数据。 [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteCsvFile)] +- **WriteMapsToCsv** : 将map切片写入csv文件中。 + [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteMapsToCsv)] [[play](https://go.dev/play/p/dAXm58Q5U1o)] - **WriteBytesToFile** : 将 bytes 写入文件。 [[doc](https://github.com/duke-git/lancet/blob/main/docs/api/packages/fileutil.md#WriteBytesToFile)] diff --git a/docs/api/packages/fileutil.md b/docs/api/packages/fileutil.md index a5183d4..70e2499 100644 --- a/docs/api/packages/fileutil.md +++ b/docs/api/packages/fileutil.md @@ -45,6 +45,7 @@ import ( - [Sha](#Sha) - [ReadCsvFile](#ReadCsvFile) - [WriteCsvFile](#WriteCsvFile) +- [WriteMapsToCsv](#WriteMapsToCsv) - [WriteStringToFile](#WriteStringToFile) - [WriteBytesToFile](#WriteBytesToFile) - [ReadFile](#ReadFile) @@ -754,7 +755,8 @@ func main() { // records: 写入文件的map切片。map值必须为基本类型。会以map键的字母顺序写入。 // appendToExistingFile: 是否为追加写模式。 // delimiter: CSV文件分割符。 -func WriteMapsToCsv(filepath string, records []map[string]string, append_to_existing_file bool, delimiter ...rune) error +// headers: CSV文件表头顺序(需要与map key保持一致),不指定时按字母排序。 +func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error ``` 示例: @@ -779,7 +781,8 @@ func main() { {"Name": "Jim", "Age": "21", "Gender": "male"}, } - err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';') + headers := []string{"Name", "Age", "Gender"} + err := WriteMapsToCsv(csvFilePath, records, false, ';', headers) if err != nil { log.Fatal(err) @@ -790,7 +793,7 @@ func main() { fmt.Println(content) // Output: - // [[Age Gender Name] [22 female Lili] [21 male Jim]] + // [[Name Age Gender] [Lili 22 female] [Jim 21 male]] } ``` diff --git a/docs/en/api/packages/fileutil.md b/docs/en/api/packages/fileutil.md index a46be12..593062e 100644 --- a/docs/en/api/packages/fileutil.md +++ b/docs/en/api/packages/fileutil.md @@ -45,6 +45,7 @@ import ( - [Sha](#Sha) - [ReadCsvFile](#ReadCsvFile) - [WriteCsvFile](#WriteCsvFile) +- [WriteCsvFile](#WriteCsvFile) - [WriteMapsToCsv](#WriteMapsToCsv) - [WriteStringToFile](#WriteStringToFile) - [WriteBytesToFile](#WriteBytesToFile) @@ -755,7 +756,8 @@ func main() { // records: slice of maps to be written. the value of map should be basic type. The maps will be sorted by key in alphabeta order, then be written into csv file. // appendToExistingFile: If true, data will be appended to the file if it exists. // delimiter: Delimiter to use in the CSV file. -func WriteMapsToCsv(filepath string, records []map[string]string, append_to_existing_file bool, delimiter ...rune) error +// headers: order of the csv column headers, needs to be consistent with the key of the map. +func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, headers ...[]string) error ``` Example: @@ -780,7 +782,8 @@ func main() { {"Name": "Jim", "Age": "21", "Gender": "male"}, } - err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';') + headers := []string{"Name", "Age", "Gender"} + err := fileutil.WriteMapsToCsv(csvFilePath, records, false, ';', headers) if err != nil { log.Fatal(err) @@ -791,7 +794,7 @@ func main() { fmt.Println(content) // Output: - // [[Age Gender Name] [22 female Lili] [21 male Jim]] + // [[Name Age Gender] [Lili 22 female] [Jim 21 male]] } ``` diff --git a/fileutil/file.go b/fileutil/file.go index a29a2a2..e8405ec 100644 --- a/fileutil/file.go +++ b/fileutil/file.go @@ -760,7 +760,9 @@ func escapeCSVField(field string, delimiter rune) string { // the maps will be sorted by key in alphabeta order, then be written into csv file. // appendToExistingFile: If true, data will be appended to the file if it exists. // delimiter: Delimiter to use in the CSV file. -func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter ...rune) error { +// headers: order of the csv column headers, needs to be consistent with the key of the map. +func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingFile bool, delimiter rune, + headers ...[]string) error { for _, record := range records { for _, value := range record { if !isCsvSupportedType(value) { @@ -769,40 +771,31 @@ func WriteMapsToCsv(filepath string, records []map[string]any, appendToExistingF } } - var datasToWrite [][]string - - // 标题(列名) - var headers []string - if len(records) > 0 { + var columnHeaders []string + if len(headers) > 0 { + columnHeaders = headers[0] + } else { for key := range records[0] { - headers = append(headers, key) + columnHeaders = append(columnHeaders, key) } + // sort keys in alphabeta order + sort.Strings(columnHeaders) } - // sort keys in alphabeta order - sort.Strings(headers) - - // 追加模式不重复写字段名 + var datasToWrite [][]string if !appendToExistingFile { - datasToWrite = append(datasToWrite, headers) + datasToWrite = append(datasToWrite, columnHeaders) } for _, record := range records { var row []string - for _, header := range headers { - row = append(row, fmt.Sprintf("%v", record[header])) + for _, h := range columnHeaders { + row = append(row, fmt.Sprintf("%v", record[h])) } datasToWrite = append(datasToWrite, row) } - var sep rune - if len(delimiter) > 0 { - sep = delimiter[0] - } else { - sep = ',' - } - - return WriteCsvFile(filepath, datasToWrite, appendToExistingFile, sep) + return WriteCsvFile(filepath, datasToWrite, appendToExistingFile, delimiter) } // check if the value of map which to be written into csv is basic type. @@ -814,8 +807,3 @@ func isCsvSupportedType(v interface{}) bool { return false } } - -// sort map by key in alphabeta order. -// func sortMap(records []map[string]any) []map[string]any { - -// } diff --git a/fileutil/file_example_test.go b/fileutil/file_example_test.go index 1932831..9fad0dc 100644 --- a/fileutil/file_example_test.go +++ b/fileutil/file_example_test.go @@ -339,7 +339,8 @@ func ExampleWriteMapsToCsv() { {"Name": "Jim", "Age": "21", "Gender": "male"}, } - err := WriteMapsToCsv(csvFilePath, records, false, ';') + headers := []string{"Name", "Age", "Gender"} + err := WriteMapsToCsv(csvFilePath, records, false, ';', headers) if err != nil { log.Fatal(err) @@ -350,7 +351,7 @@ func ExampleWriteMapsToCsv() { fmt.Println(content) // Output: - // [[Age Gender Name] [22 female Lili] [21 male Jim]] + // [[Name Age Gender] [Lili 22 female] [Jim 21 male]] } func ExampleWriteStringToFile() { diff --git a/fileutil/file_test.go b/fileutil/file_test.go index 5750670..1c0151b 100644 --- a/fileutil/file_test.go +++ b/fileutil/file_test.go @@ -397,7 +397,8 @@ func TestWriteMapsToCsv(t *testing.T) { {"Name": "Jim", "Age": "21", "Gender": "male"}, } - err := WriteMapsToCsv(csvFilePath, records, false, ';') + headers := []string{"Name", "Age", "Gender"} + err := WriteMapsToCsv(csvFilePath, records, false, ';', headers) assert.IsNil(err) @@ -407,9 +408,9 @@ func TestWriteMapsToCsv(t *testing.T) { assert.Equal(3, len(content)) assert.Equal(3, len(content[0])) - assert.Equal("22", content[1][0]) - assert.Equal("female", content[1][1]) - assert.Equal("Lili", content[1][2]) + assert.Equal("Lili", content[1][0]) + assert.Equal("22", content[1][1]) + assert.Equal("female", content[1][2]) } func TestWriteStringToFile(t *testing.T) { diff --git a/fileutil/testdata/test3.csv b/fileutil/testdata/test3.csv index aab46b6..d5e2f12 100644 --- a/fileutil/testdata/test3.csv +++ b/fileutil/testdata/test3.csv @@ -1,3 +1,3 @@ -Age;Gender;Name -22;female;Lili -21;male;Jim +Name;Age;Gender +Lili;22;female +Jim;21;male diff --git a/fileutil/testdata/test4.csv b/fileutil/testdata/test4.csv index aab46b6..d5e2f12 100644 --- a/fileutil/testdata/test4.csv +++ b/fileutil/testdata/test4.csv @@ -1,3 +1,3 @@ -Age;Gender;Name -22;female;Lili -21;male;Jim +Name;Age;Gender +Lili;22;female +Jim;21;male