Skip to content

Instantly share code, notes, and snippets.

@murphysean
Last active July 25, 2018 04:49
Show Gist options
  • Save murphysean/7604db9f7d88511170f7b2147fda338e to your computer and use it in GitHub Desktop.
Save murphysean/7604db9f7d88511170f7b2147fda338e to your computer and use it in GitHub Desktop.
CSV Code Challenge

CSV Code Challenge

Introduction

To get started run:

go get
go build
./treetop-codechallenge
curl localhost:8080/organizations | python -m json.tool
curl 'localhost:8080/organizations?id=1'
curl 'localhost:8080/organizations?state=CA'
curl 'localhost:8080/organizations?state=CA&orderby=City&direction=ASC'

To run test suite:

go get
go test -v

Requirements

Write an API endpoint that returns a filtered set of organizations from the data provided: https://s3-us-west-2.amazonaws.com/sample-coding-dataset/organization_sample_data.csv Example API:

GET /organizations?category=Greek&city=Washington
Fields:
Id: numeric id
Name: string //organization name
City: string //US city name
State: string //US state name
Postal: string //US postal code
Category: string //categorization of org
Additional query params:
Orderby: string //fieldname to order the results by
Direction: string //ASC or DSC

The expected response is a JSON collection of organizations:

{
	"organizations": [
		{
			"id":"102",
			"name":"Sigma Kappa Zeta Chapter",
			"city":"Washington",
			"state":"DC",
			"postal":"20052",
			"category":"Greek"
		},
		...
	]
}

All query parameters are optional.

Design

  • After downloading the csv, the size is negligable enough to read at startup and then keep in memory for query
  • I plan to use the encoding/csv package to read in the data
  • I plan to use the sort package to sort the objects
  • I'll keep an initial slice with all the objects serialized from the csv
  • I'll create a new slice and reference the original object in the filtered and sorted slice
  • If no direction is specified, or something other than ASC or DSC then ASC will be the default

Testing

I decided to break down the project into these testable components:

  1. Parsing the CSV
  2. Filtering
  3. Sorting

I wrote some simple tests that ensure that I properly parse the downloaded csv document. I also wrote tests around filtering and sorting.

Implementation

  • On bootup I'll download and parse the csv file into a slice of Organization References
  • I'll then start serving on port 8080, and have a Handler that filters and sorts a new array.
  • Ran into difficulties with the csv, it was using only carriage return characters, with no new line chars, thus not conforming to the standard
  • Decided to just download and modify the dataset locally, used the vim command: :%s/\r/\r/g

Improvements

Ideally this information would be stored in some sort of database that could do the filtering and sorting through SQL. I could improve performance by storing references in datastructures like trees or maps to optimize lookups for certain attributes. I also could write some better tests that would cover my code more comprehensively. It also looks like some null values are getting through, I could account for that in the csv import

package main
import (
"net/url"
"strconv"
)
func (oh *OrganizationsHandler) Filter(v url.Values) []*Organization {
os := make([]*Organization, 0)
for _, o := range oh.Organizations {
match := true
if v.Get("id") != "" &&
v.Get("id") != strconv.Itoa(o.ID) {
match = false
}
if v.Get("name") != "" &&
v.Get("name") != o.Name {
match = false
}
if v.Get("city") != "" &&
v.Get("city") != o.City {
match = false
}
if v.Get("state") != "" &&
v.Get("state") != o.State {
match = false
}
if v.Get("postal") != "" &&
v.Get("postal") != o.Postal {
match = false
}
if v.Get("category") != "" &&
v.Get("category") != o.Category {
match = false
}
if match {
os = append(os, o)
}
}
return os
}
package main
import (
"net/url"
"testing"
)
func TestFilterIDs(t *testing.T) {
values := url.Values{}
id1 := new(Organization)
id1.ID = 1
id2 := new(Organization)
id2.ID = 2
ids := []*Organization{id1, id2}
oh := NewOrganizationsHandler(ids)
values.Set("id", "1")
ids = oh.Filter(values)
if len(ids) != 1 {
t.Error("Should be only one filtered org")
}
}
package main
import (
"encoding/json"
"net/http"
)
type OrganizationsHandler struct {
Organizations []*Organization
}
func NewOrganizationsHandler(os []*Organization) *OrganizationsHandler {
oh := new(OrganizationsHandler)
oh.Organizations = os
return oh
}
func (oh *OrganizationsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
//Filter the objects
os := oh.Filter(r.URL.Query())
//Sort the objects
os = Sort(r.URL.Query(), os)
e := json.NewEncoder(w)
e.Encode(&os)
}
package main
import (
"fmt"
"net/http"
"os"
)
type Organization struct {
//Id numeric id
ID int `json:"id"`
//Name organization name
Name string `json:"name"`
//City US city name
City string `json:"city"`
//US state name
State string `json:"state"`
//US postal code
Postal string `json:"postal"`
//categorization of org
Category string `json:"category"`
}
func main() {
//On startup download and parse the csv file
f, err := os.Open("sampledata.csv")
if err != nil {
fmt.Println(err)
return
}
ozs, err := parse(f)
if err != nil {
fmt.Println(err)
return
}
//Create a new handler
oh := NewOrganizationsHandler(ozs)
//Attach it to http
http.Handle("/organizations", oh)
http.ListenAndServe(":8080", nil)
}
package main
import (
"encoding/csv"
"io"
"strconv"
)
// parse will take an io reader, run through it and build an array of objects
func parse(r io.Reader) ([]*Organization, error) {
os := make([]*Organization, 0)
cr := csv.NewReader(r)
for {
record, err := cr.Read()
if err == io.EOF {
break
}
if err != nil {
return os, err
}
o := new(Organization)
o.ID, _ = strconv.Atoi(record[0])
o.Name = record[1]
o.City = record[2]
o.State = record[3]
o.Postal = record[4]
o.Category = record[5]
if o.ID > 0 {
os = append(os, o)
}
}
return os, nil
}
package main
import (
"os"
"testing"
)
func TestParse(t *testing.T) {
f, err := os.Open("sampledata.csv")
if err != nil {
t.Fatal(err)
}
defer f.Close()
organizations, err := parse(f)
if err != nil {
t.Fatal(err)
}
if len(organizations) != 200 {
t.Error("Organizations must contain 200 objects")
}
for _, o := range organizations {
if o.ID <= 0 {
t.Error("Organization should have a positive id")
}
if o.Name == "" {
t.Error("Organization should have a name")
}
}
}
id name city state postal category
1 Michigan Science Center Detroit MI 48202 Non-Profit
2 KNSJ Community radio San Diego CA 92105 Community
3 Best Buddies Indianapolis IN 46250 Non-Profit
4 QTP Community Organization Port Saint Lucie FL 34986 Non-Profit
5 QTP Community Organization 2 Port Saint Lucie FL 34986 Greek
6 QTP Institutional Partner 2 Port Saint Lucie FL 34953 Education
7 American Red Cross Indianapolis IN 46202 Non-Profit
8 College Mentors for Kids Indianapolis IN 46202 Non-Profit
9 Damien Center Indianapolis IN 46201 Non-Profit
10 Hawthorn Community Center Indianapolis IN 46222 Community
11 Indiana Department of Corrections Indianapolis IN 46204 Government
12 Indianapolis Monumental Marathon Indianapolis IN 46204 Non-Profit
13 Marion County Prosecutor's Office Indianapolis IN 46204 Government
14 Marion County Public Health Department Indianapolis IN 46205 Government
15 National Kidney Foundation Indianapolis IN 46240 Non-Profit
16 Worldwide Health Services WEST PALM BEACH FL 33409 For-Profit
17 Emaculate cleaning institute Greensboro NC 27283 Education
18 Indiana University–Purdue University Indianapolis ( IUPUI TCEM ) Indianapolis IN 46202 Education
19 Trinity AME Zion YMS NULL NULL NULL Community
20 Kids Need More Art Jupiter FL 33458 Education
21 WGHS Blood Drive Greensboro NC 27410 Education
22 Timeless Port Saint Lucie West FL 34986 Community
23 North River Organization Port Saint Lucie West FL 34986 Non-Profit
24 QTP Institutional Partner Port Saint Lucie FL 34953 Education
25 Zionville School District Zionsville IN 46077 Education
26 Holy Family Shelter Indianapolis IN 46222 Community
27 Horizon House Indianapolis IN 46202 Community
28 HOSTS Mentoring Program Indianapolis IN 46214 Education
29 Indiana Mission of Mercy Indianapolis IN 46227 Non-Profit
30 Indianapolis Public Schools Indianapolis IN 46204 Education
31 Indy Parks and Recreation Indianapolis IN 46204 Government
32 National Alliance for Mental Illness Indianapolis IN 46222 Non-Profit
33 Shelter Sealant Program Indianapolis IN 46202 Non-Profit
34 QTP Community Organization Port Saint Lucie FL 34986 Non-Profit
35 QTP Institutional Partner Port Saint Lucie FL 34953 Education
36 Gleaner's Food Bank Indianapolis IN 46241 Community
37 Indiana Black Expo Indianapolis IN 46208 Non-Profit
38 Julian Center Indianapolis IN 46202 Non-Profit
39 Marion County Coroners Indianapolis IN 46225 Government
40 Mary Rigg Neighborhood Center Indianapolis IN 46221 Non-Profit
41 QTP Institutional Partner 2 Port Saint Lucie FL 34953 Education
42 QTP Community Organization 2 Port Saint Lucie FL 34986 Greek
43 Carmel Clay Parks & Recreation Indianapolis IN 46032 Government
44 Concord Neighborhood Center Indianapolis IN 46225 Non-Profit
45 Ivy Tech Community College Indianapolis IN 46208 Education
46 Keep Indianapolis Beautiful Indianapolis IN 46203 Non-Profit
47 PAWS Pantry Indianapolis IN 46202 Education
48 United Way of Central Indiana Indianapolis IN 46208 Non-Profit
49 Wheeler Mission Ministries Indianapolis IN 46204 Non-Profit
50 North River 3 Port Saint Lucie FL 34986 Education
51 Greensboro Symphony Orchestra Greensboro NC 27401 Non-Profit
52 Greensboro Symphony Orchestra Greensboro NC 27401 Non-Profit
53 QA Education 1 Warwick RI 2886 Education
54 Greensboro Public Libraries Greensboro NC 27401 Government
55 Young Entrepreneurs for Leadership & Sustainability Gainesville FL 32611 Education
56 Aging, Disability, and Transit Services of Rockingham County Reidsville NC 27320 Non-Profit
57 Backpack Beginnings Greensboro NC 27407 Non-Profit
58 Alamance Makers Guild Burlington NC 27215 Community
59 Holy Cross Catholic Church Kernersville NC 27284 Non-Profit
60 Guilford County Schools Greensboro NC 27401 Education
61 Parkland High School Winston-Salem NC 27127 Education
62 The Forge Greensboro Greensboro NC 27401 Non-Profit
63 Catholic Charities Diocese of Charlotte Greensboro NC 27408 Non-Profit
64 Glen Haven Community Center Greensboro NC 27405 Community
65 Iglesia De Cristo (Brewer Road Church of Christ) Winston-Salem NC 27127 Non-Profit
66 New Arrivals Institute Greensboro NC 27403 Non-Profit
67 New Arrivals Institute Greensboro NC 27403 Non-Profit
68 American Association of University Women Washington DC DC 20036 Non-Profit
69 Guilford Education Alliance Greensboro NC 27282 Non-Profit
70 Triad Workforce Solutions Collaborative Greensboro NC 27401 Community
71 Greensboro Partnership Chamber of Commerce Greensboro NC 27401 Non-Profit
72 Partners Ending Homelessness Greensboro NC 27405 Non-Profit
73 The Servant Center Greensboro NC 27403 Non-Profit
74 Horsepower Therapeutic Learning Center Colfax NC 27235 Non-Profit
75 Greensboro News and Record Greensboro NC 27401 For-Profit
76 Action Greensboro Greensboro NC 27401 Non-Profit
77 Communities in Schools of Greater Greensboro Greensboro NC 27401 Non-Profit
78 Greensboro Urban Ministry Greensboro NC 27406 Non-Profit
79 New Arrivals Institute Greensboro NC 27403 Non-Profit
80 Community Foundation of Greater Greensboro Greensboro NC 27401 Community
81 Women's Resource Center of Greensboro Greensboro NC 27405 Non-Profit
82 United Way of Greater Greensboro Greensboro NC 27405 Non-Profit
83 High Point Museum High Point NC 27262 Non-Profit
84 Interactive Resource Center Greensboro NC 27401 Non-Profit
85 Mental Health Association of Greensboro Greensboro NC 27401 Non-Profit
86 The Bicycle Tree Santa Ana CA 92701 Non-Profit
87 The Bicycle Tree Santa Ana CA 92701 Non-Profit
88 The Bicycle Tree Santa Ana CA 92701 Non-Profit
89 The Bicycle Tree Santa Ana CA 92701 Non-Profit
90 The Bicycle Tree Santa Ana CA 92701 Non-Profit
91 ArtsGreensboro Greensboro NC 27401 Non-Profit
92 Greensboro Urban Ministry Greensboro NC 27406 Non-Profit
93 Greensboro Urban Ministry Greensboro NC 27406 Non-Profit
94 ArtsGreensboro Greensboro NC 27401 Non-Profit
95 Peachtree Ridge High School Suwanee GA 30024 Education
96 Pourhouse, Inc. Indianapolis IN 46202 Non-Profit
97 YMCA Magnolia NC 33221 Non-Profit
98 Church World Service Greensboro Greensboro NC 27401 Non-Profit
99 YMCA Magnolia NC 33221 Non-Profit
100 Church World Service Greensboro Greensboro NC 27401 Non-Profit
101 SMA Color Guard Bartow FL 33830 Non-Profit
102 Sigma Kappa Zeta Chapter Washington DC 20052 Greek
103 Test School Lakeland FL 33806 Education
104 City of Lakeland Employee Engagement Lakeland FL 33801 Government
105 Lake Gibson High School Lakeland FL 33809 Education
106 Doris Henderson Newcomers School Greensboro NC 27410 Education
107 South Loop Elementary School Chicago IL 60605 Education
108 The Academy at Smith Greensboro NC 27407 Education
109 Goodwill Industries of Central North Carolina, Inc. Greensboro NC 27406 Non-Profit
110 City of Greensboro Parks & Recreation Department Greensboro NC 27410 Non-Profit
111 NCCJ - Triad Greensboro NC 27401 Non-Profit
112 Greensboro Youth Council Greensboro NC 27405 Non-Profit
113 Cobb Theatres winter haven FL 33880 For-Profit
114 Westhills Champions Baseball West Hills CA 91364 Non-Profit
115 Test Org lakeland FL 33351 Civic
116 The Middle College at NC A&T Greensboro NC 27401 Education
117 TCC High Point NC 27401 Non-Profit
118 Rebuild Polk After Disaster Lake Wales FL 33853 Non-Profit
119 Happy Hills Staley NC 27355 Non-Profit
120 Wings of Eagles International Lakeland FL 33809 Non-Profit
121 High Point Central High School High Point NC 27262 Education
122 Acorn Sign Language Prescott AZ 86305 Civic
123 Charity Accelerator.org Phoenix AZ 85032 Non-Profit
124 Pine Belt 360 Sumrall MS 39482 Non-Profit
125 United Way Hands On Greenville Greenville SC 29607 Non-Profit
126 University of Central Oklahoma Edmond OK 73034 Education
127 South Carolina Campus Compact Rock Hil SC 29733 For-Profit
128 Student Services Assistant - MC@ NC A&T Greensboro NC 27411 Education
129 Student Recruitment Services Greensboro NC 27411 Education
130 Southern Guilford High School Greensboro NC 27406 Education
131 Albany Leadership Charter High School for Girls Albany NY 12208 Education
132 Adams Farm Living & Rehab Jamestown NC 27282 For-Profit
133 Adult Center for Enrichment Greensboro NC 27415 Non-Profit
134 American Red Cross Greater Greensboro Greensboro NC 27405 Non-Profit
135 Frank Orlando FL 32836 Education
136 Greenville Technical College Greenville SC 29607 Education
137 Greensboro Urban Ministry Greensboro NC 27406 Non-Profit
138 Sgt. Gamers San Jose CA 95117 Civic
139 HORSEPOWER Colfax NC 27235 Non-Profit
140 Backpack Beginnings Greensboro/High Point NC 27407 Non-Profit
141 Griffin Baptist Church Lakeland FL 33805 Non-Profit
142 Friends in Action Greensboro NC 27438 Non-Profit
143 ArtQuest at Green Hill Center for NC Greensboro NC 27401 Non-Profit
144 Birmingham Urban League Young Professionals Birmingham AL 35203 Non-Profit
145 Dallas County Community College District Service Learning Dallas TX 75215 Education
146 Lily Conference Greensboro NC 27408 Education
147 Black & Latino Achievers Greensboro NC 27401 Civic
148 Tristan's Quest Greensboro NC 27409 Non-Profit
149 TRU Youth Advisory Board Greensboro NC 27405 Government
150 Teen Grantmqking Council of the Community Foundation of Greater Greensboro Greensboro NC 27401 Non-Profit
151 OCSL Alternative Break Club Hattiesburg MS 39406 Civic
152 Lycoming College Williamsport PA 17701 Education
153 Northwest Guilford High School High Point NC 27409 Education
154 City of High Point Human Relations High Point NC 27261 Government
155 High Point Parks & Recreation High Point NC 27262 Government
156 Polk Gives Lake Alfred FL 33850 Non-Profit
157 Genesis Baptist Church Greensboro NC 27405 Non-Profit
158 The International Civil Rights Center & Museum Greensboro NC 27401 Non-Profit
159 My COmmunity Los Angeles CA 90210 Civic
160 Northeast High School Mcleansville NC 27310 Education
161 Guilford County Animal Shelter Greensboro NC 27409 Non-Profit
162 Generous Garden Project Greenville SC 29607 Non-Profit
163 Boy Scout Troop 441 Greensboro NC 27403 Non-Profit
164 School Supplies for Belize Jamestown NC 27282 Education
165 Diamonds in the Rough Snellville GA 30078 Non-Profit
166 St. Mary's Catholic Church Greensboro NC 27401 Non-Profit
167 Saint Mary's Catholic Church Greensboro NC 27401 Non-Profit
168 Western Guilford Volleyball Greensboror NC 27410 Civic
169 Seattle University Youth Initiative Seattle WA 98122 Non-Profit
170 Students For Justice in Palestine Boca Raton FL 33431 Civic
171 George W. Jenkins Senior High LAKELAND FL 33813 Education
172 Tenoroc High School Lakeland FL 33801 Education
173 Lake Gibson Senior High School LAKELAND FL 33809 Education
174 EL KAU KAU KONER Lakeland FL 33801 For-Profit
175 AAA air conditioning auburndale FL 33823 For-Profit
176 Famliy Fun Center Lakeland FL 33811 For-Profit
177 Adams Floral Shop Lakeland FL 33803 For-Profit
178 Holy Name Of Jesus School INDIALANTIC FL 32903 Education
179 Wings of Eagles International Outreach Center of Central Florida Lakeland FL 33809 Non-Profit
180 Steak n' Shake Lakeland FL 33813 For-Profit
181 Haines City Senior High School HAINES CITY FL 33844 Education
182 Healthy Start Coalition of Hardee, Highlands & Polk Counties, Inc Bartow FL 33830 Non-Profit
183 Pop Some Culture Lakeland FL 33813 For-Profit
184 Guardian Ad Litem Lake City FL 32055 Non-Profit
185 ElderCare of Alachua County, Inc. Gainesville FL 32608 Non-Profit
186 University Of North Florida Jacksonville FL 32224 Education
187 Off The Wall Adventures Lakeland FL 33811 For-Profit
188 Child Advocacy Center Gainesville FL 32602 Non-Profit
189 William T. Dwyer High School - Academy of Finance PALM BEACH GARDENS FL 33418 Education
190 american insurance center of bartow bartow FL 33830 For-Profit
191 Lakeland Senior High School LAKELAND FL 33803 Education
192 The Barn Antiques Lake Alfred FL 33850 For-Profit
193 Miami Dade College Miami FL 33132 Education
194 TruLine Construction Services Lakeland FL 33809 For-Profit
195 Texas Campus Compact Austin TX 78701 Non-Profit
196 JC Penny Lake Wales FL 33859 For-Profit
197 Publix lakeland FL 33813 For-Profit
198 Trent Miller babson park FL 33827 For-Profit
199 Tobacco Free Lee Fort Myers FL 33916 Non-Profit
200 Mulberry High School Key Club Mulberry FL 33860 Civic
package main
import (
"net/url"
"sort"
)
func Sort(v url.Values, os []*Organization) []*Organization {
by := v.Get("orderby")
dir := v.Get("direction")
if dir == "" || dir != "DSC" {
dir = "ASC"
}
switch by {
case "id":
if dir == "ASC" {
sort.Slice(os, func(i, j int) bool { return os[i].ID < os[j].ID })
} else {
sort.Slice(os, func(i, j int) bool { return os[i].ID > os[j].ID })
}
case "name":
if dir == "ASC" {
sort.Slice(os, func(i, j int) bool { return os[i].Name < os[j].Name })
} else {
sort.Slice(os, func(i, j int) bool { return os[i].Name > os[j].Name })
}
case "city":
if dir == "ASC" {
sort.Slice(os, func(i, j int) bool { return os[i].City < os[j].City })
} else {
sort.Slice(os, func(i, j int) bool { return os[i].City > os[j].City })
}
case "state":
if dir == "ASC" {
sort.Slice(os, func(i, j int) bool { return os[i].State < os[j].State })
} else {
sort.Slice(os, func(i, j int) bool { return os[i].State > os[j].State })
}
case "postal":
if dir == "ASC" {
sort.Slice(os, func(i, j int) bool { return os[i].Postal < os[j].Postal })
} else {
sort.Slice(os, func(i, j int) bool { return os[i].Postal > os[j].Postal })
}
case "category":
if dir == "ASC" {
sort.Slice(os, func(i, j int) bool { return os[i].Category < os[j].Category })
} else {
sort.Slice(os, func(i, j int) bool { return os[i].Category > os[j].Category })
}
}
return os
}
package main
import (
"net/url"
"testing"
)
func TestSortIDsDSC(t *testing.T) {
//Test that id is sorted correctly
values := url.Values{}
id1 := new(Organization)
id1.ID = 1
id2 := new(Organization)
id2.ID = 2
ids := []*Organization{id1, id2}
values.Set("orderby", "id")
values.Set("direction", "DSC")
ids = Sort(values, ids)
if ids[0].ID < ids[1].ID {
t.Error("Ids not sorted in DSC correctly")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment