-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconcat.go
More file actions
124 lines (117 loc) · 3.82 KB
/
Copy pathconcat.go
File metadata and controls
124 lines (117 loc) · 3.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package simplecsv
import (
"errors"
"fmt"
)
// Concat returns a new csv with the data rows of all the csvs stacked
// vertically: the result header row is the header row of the first
// non-empty csv, followed by copies of the data rows of every csv in
// argument order. Every csv with a header row must have exactly the same
// header names in the same order; empty csvs (without a header row) are
// skipped. The original csvs are not modified and the result shares no
// data with them. If there is no non-empty csv, or two csvs have different
// headers, it returns nil and false.
func Concat(csvs ...SimpleCsv) (SimpleCsv, bool) {
result, err := ConcatE(csvs...)
return result, err == nil
}
// ConcatE is like Concat, but it returns an error describing the reason of
// the failure instead of false. On failure it returns nil.
func ConcatE(csvs ...SimpleCsv) (SimpleCsv, error) {
var headers SimpleCsv
first := -1
for i, c := range csvs {
if len(c) > 0 {
first = i
headers = c
break
}
}
if first == -1 {
return nil, errors.New("simplecsv: cannot concat: no csv with a header row")
}
result := SimpleCsv{copyRow(headers[0])}
for i, c := range csvs {
if len(c) == 0 {
continue
}
if !headers.sameHeaders(c) {
return nil, fmt.Errorf("simplecsv: cannot concat: csv at position %d has different headers than the first csv", i)
}
for j := 1; j < len(c); j++ {
result = append(result, copyRow(c[j]))
}
}
return result, nil
}
// ConcatByName returns a new csv with the data rows of all the csvs stacked
// vertically, aligning the columns by header name instead of by position:
// the result header row is the header row of the first non-empty csv,
// followed by the header names that only appear in later csvs, in order of
// first appearance. Each data row of every csv is copied into the result
// with the value of each column placed under its header name, and an empty
// string where the csv has no column with that name. Empty csvs (without a
// header row) are skipped. The original csvs are not modified and the
// result shares no data with them. If there is no non-empty csv, or any csv
// has duplicate header names (the result would be ambiguous and break the
// unique-header invariant), it returns nil and false.
func ConcatByName(csvs ...SimpleCsv) (SimpleCsv, bool) {
result, err := ConcatByNameE(csvs...)
return result, err == nil
}
// ConcatByNameE is like ConcatByName, but it returns an error describing
// the reason of the failure instead of false. On failure it returns nil.
func ConcatByNameE(csvs ...SimpleCsv) (SimpleCsv, error) {
var headers []string
first := -1
for i, c := range csvs {
if len(c) == 0 {
continue
}
if hasDuplicateHeaders(c[0]) {
return nil, fmt.Errorf("simplecsv: cannot concat by name: csv at position %d has duplicate header names", i)
}
first = i
break
}
if first == -1 {
return nil, errors.New("simplecsv: cannot concat by name: no csv with a header row")
}
position := make(map[string]int, len(csvs[first][0]))
for k, header := range csvs[first][0] {
position[header] = k
}
headers = copyRow(csvs[first][0])
for i, c := range csvs[first+1:] {
if len(c) == 0 {
continue
}
if hasDuplicateHeaders(c[0]) {
return nil, fmt.Errorf("simplecsv: cannot concat by name: csv at position %d has duplicate header names", first+1+i)
}
for _, header := range c[0] {
if _, exists := position[header]; !exists {
position[header] = len(headers)
headers = append(headers, header)
}
}
}
result := SimpleCsv{headers}
for _, c := range csvs {
if len(c) == 0 {
continue
}
for i := 1; i < len(c); i++ {
row := make([]string, len(headers))
for k, value := range c[i] {
if k < len(c[0]) {
if p, exists := position[c[0][k]]; exists {
row[p] = value
}
}
}
result = append(result, row)
}
}
return result, nil
}