Skip to content

Commit 63ccf81

Browse files
author
thisisaaronland
committed
initial commit
0 parents  commit 63ccf81

8 files changed

+313
-0
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*~
2+
pkg
3+
src
4+
bin/index
5+
bin/index-csv
6+
bin/pip-server

LICENSE

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Copyright (c) 2021, City and County of San Francisco, acting by and through its
2+
Airport Commission ("City"). All rights reserved.
3+
4+
The City and County of San Francisco, acting by and through its Airport
5+
Commission, created and operates the SFO Museum.
6+
7+
Redistribution and use in source and binary forms, with or without modification,
8+
are permitted provided that the following conditions are met:
9+
10+
1. Redistributions of source code must retain the above copyright notice, this
11+
list of conditions and the following disclaimer.
12+
2. Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation
14+
and/or other materials provided with the distribution.
15+
3. Neither the name of the City nor the names of its contributors may be used
16+
to endorse or promote products derived from this software without specific
17+
prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Makefile

Whitespace-only changes.

README.md

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# go-csvdict
2+
3+
Go package to implement a "dict reader" style CSV parser (on top of the default `encoding/csv` package) to return rows a key-value dictionaries rather than lists.
4+
5+
## Documentation
6+
7+
[![Go Reference](https://pkg.go.dev/badge/github.com/sfomuseum/go-csvdict.svg)](https://pkg.go.dev/github.com/sfomuseum/go-csvdict)
8+
9+
Documentation is incomplete at this time.
10+
11+
## Example
12+
13+
### Reading files
14+
15+
```
16+
import (
17+
"github.com/whosonfirst/go-csvdict"
18+
"os"
19+
)
20+
21+
reader, reader_err := csvdict.NewReaderFromPath("example.csv")
22+
23+
// or maybe you might do
24+
// reader, err := csvdict.NewReader(os.Stdin)
25+
26+
if err != nil {
27+
panic(err)
28+
}
29+
30+
for {
31+
row, err := reader.Read()
32+
33+
if err == io.EOF {
34+
break
35+
}
36+
37+
if err != nil {
38+
return err
39+
}
40+
41+
value, ok := row["some-key"]
42+
43+
// and so on...
44+
}
45+
```
46+
47+
### Writing files
48+
49+
```
50+
import (
51+
"github.com/whosonfirst/go-csvdict"
52+
"os"
53+
)
54+
55+
fieldnames := []string{"foo", "bar"}
56+
57+
writer, err := csvdict.NewWriter(os.Stdout, fieldnames)
58+
59+
// or maybe you might do
60+
// writer, err := csvdict.NewWriterFromPath("new.csv", fieldnames)
61+
62+
if err != nil {
63+
panic(err)
64+
}
65+
66+
writer.WriteHeader()
67+
68+
row := make(map[string]string)
69+
row["foo"] = "hello"
70+
row["bar"] = "world"
71+
72+
// See this? "baz" is not included in the list of fieldnames
73+
// above so it will be silently ignored and excluded from your
74+
// CSV file. Perhaps it should trigger an error. It doesn't, today...
75+
76+
row["baz"] = "wub wub wub"
77+
78+
writer.WriteRow(row)
79+
```
80+
81+
## Tools
82+
83+
### wof-csv-filter
84+
85+
Concatenate one or more CSV files ensuring a common set of columns.
86+
87+
```
88+
./bin/wof-csv-filter -h
89+
Usage of wof-csv-filter:
90+
$> wof-csv-filter -options <files>
91+
92+
Valid options are:
93+
-columns string
94+
Columns to filter on. A value of "-" means the set of unique columns for all CSV files being filtered. (default "-")
95+
-out string
96+
Where to write the data. A value of "-" means write the data to STDOUT. (default "-")
97+
```
98+
99+
## See also
100+
101+
* https://golang.org/pkg/encoding/csv/
102+
* https://docs.python.org/2/library/csv.html

csvdict.go

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package csvdict
2+
3+
import (
4+
"encoding/csv"
5+
"io"
6+
"os"
7+
)
8+
9+
type Reader struct {
10+
Reader *csv.Reader
11+
Fieldnames []string
12+
}
13+
14+
type Writer struct {
15+
Writer *csv.Writer
16+
Fieldnames []string
17+
}
18+
19+
func NewReader(fh io.Reader) (*Reader, error) {
20+
21+
reader := csv.NewReader(fh)
22+
23+
row, read_err := reader.Read()
24+
25+
if read_err != nil {
26+
return nil, read_err
27+
}
28+
29+
dr := Reader{Reader: reader, Fieldnames: row}
30+
return &dr, nil
31+
}
32+
33+
func NewReaderFromPath(path string) (*Reader, error) {
34+
35+
fh, err := os.Open(path)
36+
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
return NewReader(fh)
42+
}
43+
44+
func (dr Reader) Read() (map[string]string, error) {
45+
46+
row, err := dr.Reader.Read()
47+
48+
if err != nil {
49+
return nil, err
50+
}
51+
52+
dict := make(map[string]string)
53+
54+
for i, value := range row {
55+
key := dr.Fieldnames[i]
56+
dict[key] = value
57+
}
58+
59+
return dict, nil
60+
}
61+
62+
func NewWriter(fh io.Writer, fieldnames []string) (*Writer, error) {
63+
64+
writer := csv.NewWriter(fh)
65+
66+
dw := Writer{Writer: writer, Fieldnames: fieldnames}
67+
return &dw, nil
68+
}
69+
70+
func NewWriterFromPath(path string, fieldnames []string) (*Writer, error) {
71+
72+
fh, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
73+
74+
if err != nil {
75+
return nil, err
76+
}
77+
78+
return NewWriter(fh, fieldnames)
79+
}
80+
81+
func (dw Writer) WriteHeader() {
82+
dw.Writer.Write(dw.Fieldnames)
83+
dw.Writer.Flush()
84+
}
85+
86+
// to do - check flags for whether or not to be liberal when missing keys
87+
// (20160516/thisisaaronland)
88+
89+
func (dw Writer) WriteRow(row map[string]string) {
90+
91+
out := make([]string, 0)
92+
93+
for _, k := range dw.Fieldnames {
94+
95+
v, ok := row[k]
96+
97+
if !ok {
98+
v = ""
99+
}
100+
101+
out = append(out, v)
102+
}
103+
104+
dw.Writer.Write(out)
105+
dw.Writer.Flush() // move me somewhere more sensible
106+
}

csvdict_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package csvdict

doc.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// package csvdict implements a "dictionary reader" style CSV parser (on top of the default `encoding/csv` package) to return rows a key-value dictionaries rather than lists.
2+
// Example
3+
//
4+
// Reading files
5+
//
6+
// import (
7+
// "github.com/whosonfirst/go-csvdict"
8+
// "os"
9+
// )
10+
//
11+
// reader, reader_err := csvdict.NewReaderFromPath("example.csv")
12+
//
13+
// // or maybe you might do
14+
// // reader, err := csvdict.NewReader(os.Stdin)
15+
//
16+
// if err != nil {
17+
// panic(err)
18+
// }
19+
//
20+
// for {
21+
// row, err := reader.Read()
22+
//
23+
// if err == io.EOF {
24+
// break
25+
// }
26+
//
27+
// if err != nil {
28+
// return err
29+
// }
30+
//
31+
// value, ok := row["some-key"]
32+
//
33+
// // and so on...
34+
// }
35+
//
36+
// Writing files
37+
//
38+
// import (
39+
// "github.com/whosonfirst/go-csvdict"
40+
// "os"
41+
// )
42+
//
43+
// fieldnames := []string{"foo", "bar"}
44+
//
45+
// writer, err := csvdict.NewWriter(os.Stdout, fieldnames)
46+
//
47+
// // or maybe you might do
48+
// // writer, err := csvdict.NewWriterFromPath("new.csv", fieldnames)
49+
//
50+
// if err != nil {
51+
// panic(err)
52+
// }
53+
//
54+
// writer.WriteHeader()
55+
//
56+
// row := make(map[string]string)
57+
// row["foo"] = "hello"
58+
// row["bar"] = "world"
59+
//
60+
// // See this? "baz" is not included in the list of fieldnames
61+
// // above so it will be silently ignored and excluded from your
62+
// // CSV file. Perhaps it should trigger an error. It doesn't, today...
63+
//
64+
// row["baz"] = "wub wub wub"
65+
//
66+
// writer.WriteRow(row)
67+
package csvdict

go.mod

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module github.com/sfomuseum/go-csvdict
2+
3+
go 1.12

0 commit comments

Comments
 (0)