Skip to content

Commit 6bfc75a

Browse files
committed
feat: Add Decoder.NormalizeHeader
Closes: #45
1 parent 50fc372 commit 6bfc75a

File tree

2 files changed

+61
-0
lines changed

2 files changed

+61
-0
lines changed

decoder.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package csvutil
22

33
import (
4+
"errors"
45
"io"
56
"reflect"
67
)
@@ -202,6 +203,27 @@ func (d *Decoder) Header() []string {
202203
return header
203204
}
204205

206+
// NormalizeHeader applies f to every column in the header. It returns error
207+
// if calling f results in conflicting header columns.
208+
//
209+
// NormalizeHeader must be called before Decode.
210+
func (d *Decoder) NormalizeHeader(f func(string) string) error {
211+
set := make(map[string]int, len(d.header))
212+
for i, s := range d.header {
213+
set[f(s)] = i
214+
}
215+
216+
if len(set) != len(d.header) {
217+
return errors.New("csvutil: normalize header results in conflicting columns")
218+
}
219+
220+
for s, i := range set {
221+
d.header[i] = s
222+
}
223+
d.hmap = set
224+
return nil
225+
}
226+
205227
// Unused returns a list of column indexes that were not used during decoding
206228
// due to lack of matching struct field.
207229
func (d *Decoder) Unused() []int {

decoder_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2405,6 +2405,45 @@ s,1,3.14,true
24052405
t.Log(e)
24062406
})
24072407
})
2408+
2409+
t.Run("normalize header", func(t *testing.T) {
2410+
csvr := csv.NewReader(strings.NewReader("STRING,INT\nfirst,1"))
2411+
dec, err := NewDecoder(csvr)
2412+
if err != nil {
2413+
t.Fatalf("want err == nil; got %v", err)
2414+
}
2415+
2416+
if err := dec.NormalizeHeader(strings.ToLower); err != nil {
2417+
t.Fatalf("want err=nil; got %v", err)
2418+
}
2419+
2420+
var data struct {
2421+
String string `csv:"string"`
2422+
Int int `csv:"int"`
2423+
}
2424+
if err := dec.Decode(&data); err != nil {
2425+
t.Fatalf("want err=nil; got %v", err)
2426+
}
2427+
2428+
if data.String != "first" {
2429+
t.Errorf("want String=first; got %s", data.String)
2430+
}
2431+
if data.Int != 1 {
2432+
t.Errorf("want Int=1; got %d", data.Int)
2433+
}
2434+
})
2435+
2436+
t.Run("normalize header - duplicate error", func(t *testing.T) {
2437+
csvr := csv.NewReader(strings.NewReader("STRING,string\nfirst,1"))
2438+
dec, err := NewDecoder(csvr)
2439+
if err != nil {
2440+
t.Fatalf("want err == nil; got %v", err)
2441+
}
2442+
2443+
if err := dec.NormalizeHeader(strings.ToLower); err == nil {
2444+
t.Fatal("want err not to be nil")
2445+
}
2446+
})
24082447
}
24092448

24102449
func BenchmarkDecode(b *testing.B) {

0 commit comments

Comments
 (0)