Skip to content

Instantly share code, notes, and snippets.

@nikitamounier
Created January 7, 2022 16:59
Show Gist options
  • Save nikitamounier/00af8d60b0a9f759af4f14c1fa2be5ad to your computer and use it in GitHub Desktop.
Save nikitamounier/00af8d60b0a9f759af4f14c1fa2be5ad to your computer and use it in GitHub Desktop.
CSV <-> Array<OrderedDictionary>
/*
In Computer Science class I was tasked with writing a function takes a raw CSV table and returns a list of OrderedDictionaries and vice versa – but in Python.
It felt messy, convoluted and slow, so I wanted to what it would look like in Swift, especially with Pointfree's swift-parsing library and their experimental parser-builder branch.
This is as high performance as possible while using the swift-parsing library. Consequently, this doesn't support special characters such as é or æ due to the use of UTF8 code units.
*/
import Algorithms
import OrderedCollections
import Parsing
func csvToDictionaries(csv: String) -> [OrderedDictionary<String, String>]? {
let quoteOrField = Parse {
OneOf {
Parse {
"\"".utf8
Prefix { $0 != .init(ascii: "\"") }
"\"".utf8
}
Prefix { $0 != .init(ascii: ",") && $0 != .init(ascii: "\n") }
}
.map { String(Substring($0)) }
}
let csvParser = Parse {
Many {
Many {
quoteOrField
} separator: {
",".utf8
}
} separator: {
"\n".utf8
}
.map { input -> Array<OrderedDictionary<String, String>> in
let headers = input.first!
var output: Array<OrderedDictionary<String, String>> = []
output.reserveCapacity(input.count - 1)
input[1...].forEach { row in
output.append(OrderedDictionary(uncheckedUniqueKeys: headers, values: row))
}
return output
}
}
return csvParser.parse(csv)
}
func dictionariesToCSV(dics dictionaries: [OrderedDictionary<String, String>]) -> String {
var bytes: [UTF8.CodeUnit] = []
bytes.append(
contentsOf: dictionaries.first!.keys
.interspersed(with: ",")
.flatMap(\.utf8)
)
bytes.append(.init(ascii: "\n"))
dictionaries.map(\.values).forEach { row in
bytes.append(
contentsOf: row
.interspersed(with: ",")
.flatMap(\.utf8)
)
bytes.append(.init(ascii: "\n"))
}
return String(decoding: bytes, as: UTF8.self)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment