underscoreio/csvside

Name: csvside

Owner: Underscore

Description: CSV reader and writer combinators for Scala. Made with Cats.

Created: 2015-07-18 20:15:15.0

Updated: 2018-03-28 14:53:09.0

Pushed: 2018-03-28 14:53:10.0

Homepage:

Size: 95

Language: Scala

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

CSVSide

CSV readers and combinators for Scala. Made with Cats.

Copyright 2015 Richard Dallaway and Dave Gurnell. Licensed Apache 2.

Build Status Coverage status Maven Central

Getting Started

Grab the code by adding the following to your build.sbt:

aryDependencies += "io.underscore" %% "csvside" % "<<VERSION>>"
Synopsis
Fixed Reader Row CSV Files

Here's an example that reads directly from a String. You can also read from a java.io.File or a java.io.Reader:

e want to parse this CSV...
csv = """
tr,Bool,Int
bc,true,123
a b",false,321
,
bc,abc,abc
trim.stripMargin

o a sequence of this data structure...
 class Test(str: String, num: Int, bool: Option[Boolean])

e import Csvside...
rt csvside._

e define a RowFormat...
rt cats.syntax.cartesian._
icit val testFormat: RowFormat[Test] = (
tr".csv[String] |@|
nt".csv[Int] |@|
ool".csv[Option[Boolean]]
ap(Test.apply)(unlift(Test.unapply))

e read the data...
ans = Csv.fromString[Test](csv).toList
ns: List[csvside.CsvValidated[Test]] = List(
 Valid(Test(abc,123,Some(true))),
 Valid(Test(a b,321,Some(false))),
 Invalid(List(CsvError(4,Int,Must be a whole number))),
 Invalid(List(CsvError(5,Int,Must be a whole number),
              CsvError(5,Bool,Must be a yes/no value or blank))))

nd we write it back to CSV...
rt cats.data.Validated
validOnly = ans collect { case Validated.Valid(test) => test }
finalCsv = Csv.toString(validOnly)
inalCsv: String =
"Str","Int","Bool"
abc","123","true"
a b","321","false"

Variable Header Row CSV Files

If the format of the rows depends on the values in the header row, we can use a ListReader[A] to generate a ColumnReader[A] on the fly:

e want to parse the cells in this CSV...
csv = s"""
ow,Col1,Col2,Col3
,1,2,3
,,,
,3,,1
trim.stripMargin

o a sequence of this data structure...
 class Test(key: String, values: Map[CsvPath, Option[Int]])

e do this by creating a `ListReader` that
arses the column headings and creates a `ColumnReader`
o read the rest of the file.

e import from Csvside...
rt csvside._

e define a ListReader...
rt cats.data.Validated.{valid, invalid}
rt cats.syntax.cartesian._
rt cats.syntax.validated._
icit val testReader: ListReader[Test] =
stReader[Test] {
case head :: tail =>
  (head.read[String] |@| tail.readMap[Option[Int]]).map(Test.apply).valid

case Nil =>
  invalid(List(CsvError(1, "", "File must contain at least one column")))


nd we read the data...
ans = Csv.fromString[Test](csv).toList
ns: List[CsvValidated[Test]] =
 List(
   Valid(Test("x", Map("Col1" -> Some(1), "Col2" -> Some(2), "Col3" -> Some(3)))),
   Valid(Test("y", Map("Col1" -> None,    "Col2" -> None,    "Col3" -> None))),
   Valid(Test("z", Map("Col1" -> Some(3), "Col2" -> None,    "Col3" -> Some(1)))))

This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.