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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
package csv
import (
"fmt"
"github.com/wildducktheories/go-csv/utils"
"os"
)
//Record provides keyed access to the fields of data records where each field
//of a data record is keyed by the value of the corresponding field in the header record.
type Record interface {
// Return the header of the record.
Header() []string
// Gets the value of the field specified by the key. Returns the empty string
// if the field does not exist in the record.
Get(key string) string
// Puts the value into the field specified by the key.
Put(key string, value string)
// Return the contents of the record as a map. Mutation of the map is not supported.
AsMap() map[string]string
// Return the contents of the record as a slice. Mutation of the slice is not supported.
AsSlice() []string
// Puts all the matching values from the specified record into the receiving record
PutAll(r Record)
// Return true if the receiver and the specified record have the same header.
SameHeader(r Record) bool
}
type record struct {
header []string
index map[string]int
fields []string
cache map[string]string
}
type RecordBuilder func(fields []string) Record
// NewRecordBuilder returns a function that can be used to create new Records
// for a CSV stream with the specified header.
//
// This can be used with raw encoding/csv streams in cases where a CSV stream contains
// more than one record type.
func NewRecordBuilder(header []string) RecordBuilder {
index := utils.NewIndex(header)
return func(fields []string) Record {
if len(header) < len(fields) {
fmt.Fprintf(os.Stderr, "invariant violated: [%d]fields=%v, [%d]header=%v\n", len(fields), fields, len(header), header)
}
tmp := make([]string, len(header), len(header))
copy(tmp, fields)
return &record{
header: header,
index: index,
fields: tmp,
}
}
}
func (r *record) Header() []string {
return r.header
}
// Answer the value of the field indexed by the column containing the specified header value.
func (r *record) Get(key string) string {
x, ok := r.index[key]
if ok && x < len(r.fields) {
return r.fields[x]
}
return ""
}
// Puts the specified value into the record at the index determined by the key value.
func (r *record) Put(key string, value string) {
x, ok := r.index[key]
if ok && x < cap(r.fields) {
if x > len(r.fields) {
r.fields = r.fields[0:x]
}
if r.cache != nil {
r.cache[key] = value
}
r.fields[x] = value
}
}
// Return a map containing a copy of the contents of the record.
func (r *record) AsMap() map[string]string {
if r.cache != nil {
return r.cache
}
result := make(map[string]string)
for i, h := range r.header {
if i < len(r.fields) {
result[h] = r.fields[i]
} else {
result[h] = ""
}
}
r.cache = result
return result
}
// Return the record values as a slice.
func (r *record) AsSlice() []string {
return r.fields
}
// Puts all the specified value into the record.
func (r *record) PutAll(o Record) {
if r.SameHeader(o) {
copy(r.fields, o.AsSlice())
r.cache = nil
} else {
for i, k := range r.header {
v := o.Get(k)
r.fields[i] = v
if r.cache != nil {
r.cache[k] = v
}
}
}
}
// Efficiently check that the receiver and specified records have the same header
func (r *record) SameHeader(o Record) bool {
h := o.Header()
if len(r.header) != len(h) {
return false
} else if len(h) == 0 || &h[0] == &r.header[0] {
// two slices with the same address and length have the same contents
return true
} else {
for i, k := range r.header {
if h[i] != k {
return false
}
}
return true
}
}
|