Skip to content
127 changes: 124 additions & 3 deletions docs/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ object Main extends CommandIOApp("mkString", "Concatenates strings from stdin")

## Parsing and transforming a CSV file

Here, [fs2-data-csv] is used to read and parse a comma separated file.
Here, [fs2-data-csv] is used to read and parse a comma separated file.
Manual encoders and decoders are defined for our `Passenger`s to show you how to do everything from scratch.

Let's start with a CSV file that has records of fictious passengers registered for a flight:
Expand Down Expand Up @@ -233,7 +233,7 @@ val input = Files[IO]

object CSVPrinter extends IOApp.Simple:

/** First we'll do some logging for each row,
/** First we'll do some logging for each row,
* and then calculate and print the mean age */
val run =
input
Expand Down Expand Up @@ -310,7 +310,7 @@ object CSVPrinter extends IOApp.Simple {
.through(decodeUsingHeaders[Passenger]())


/** First we'll do some logging for each row,
/** First we'll do some logging for each row,
* and then calculate and print the mean age */
val run =
input
Expand Down Expand Up @@ -420,6 +420,127 @@ object PerftConverter extends IOApp.Simple {
```
@:@

## Writing data to a CSV file

If you want to save a list of a case class into a CSV file this utility may aid you:

@:select(scala-version)

@:choice(scala-3)
```scala
// Define your case class and derive an encoder for it
case class YourCaseClass(n: String, i: Int)
given CsvRowEncoder[YourCaseClass, String] = deriveCsvRowEncoder

// Writes a case class as a csv given a path.
def writeCaseClassToCsv[A](
path: Path
)(using CsvRowEncoder[A, String]): Pipe[IO, A, Nothing] =
_.through(encodeUsingFirstHeaders(fullRows = true))
.through(fs2.text.utf8.encode)
.through(Files[IO].writeAll(path))
```

@:choice(scala-2)
```scala
case class YourCaseClass(n: String, i: Int)
implicit val csvRowEncoder: CsvRowEncoder[YourCaseClass, String] = deriveCsvRowEncoder

object Helpers {
// Writes a case class as a csv given a path.
def writeCaseClassToCsv[A](
path: Path
)(implicit encoder: CsvRowEncoder[A, String]): Pipe[IO, A, Nothing] =
_.through(encodeUsingFirstHeaders(fullRows = true))
.through(fs2.text.utf8.encode)
.through(Files[IO].writeAll(path))
}
```
@:@

As an example, let's imagine we have a `Book` class we would like to write to a `.csv` file.

@:select(scala-version)

@:choice(scala-3)
```scala mdoc:reset:silent
//> using lib "org.typelevel::toolkit::@VERSION@"

import fs2.data.csv.*
import fs2.data.csv.generic.semiauto.*
import fs2.io.file.{Files, Path}
import cats.effect.{IO, IOApp}
import fs2.{Pipe, Stream}

def writeCaseClassToCsv[A](
path: Path
)(using CsvRowEncoder[A, String]): Pipe[IO, A, Nothing] =
_.through(encodeUsingFirstHeaders(fullRows = true))
.through(fs2.text.utf8.encode)
.through(Files[IO].writeAll(path))


object WriteBooksToCsv extends IOApp.Simple:
case class Book(id: Long, name: String, isbn: String)
given CsvRowEncoder[Book, String] = deriveCsvRowEncoder

val input = Seq(
Book(1, "Programming in Scala", "9780997148008"),
Book(2, "Hands-on Scala Programming", "9798387677205"),
Book(3, "Functional Programming in Scala", "9781617299582")
)

def run: IO[Unit] =
Stream
.emits(input)
.through(writeCaseClassToCsv(Path("books.csv")))
.compile
.drain *> IO.println("Finished writing books to books.csv.")
```

@:choice(scala-2)

```scala mdoc:reset:silent
//> using lib "org.typelevel::toolkit::@VERSION@"

import fs2.data.csv._
import fs2.data.csv.generic.semiauto._
import fs2.io.file.{Files, Path}
import cats.effect.{IO, IOApp}
import fs2.{Pipe, Stream}

object Helpers {
def writeCaseClassToCsv[A](
path: Path
)(implicit encoder: CsvRowEncoder[A, String]): Pipe[IO, A, Nothing] =
_.through(encodeUsingFirstHeaders(fullRows = true))
.through(fs2.text.utf8.encode)
.through(Files[IO].writeAll(path))
}

object WriteBooksToCsv extends IOApp.Simple {
case class Book(id: Long, name: String, isbn: String)
implicit val csvRowEncoder: CsvRowEncoder[Book, String] = deriveCsvRowEncoder

val input = Seq(
Book(1, "Programming in Scala", "9780997148008"),
Book(2, "Hands-on Scala Programming", "9798387677205"),
Book(3, "Functional Programming in Scala", "9781617299582")
)

def run: IO[Unit] =
Stream
.emits(input)
.through(Helpers.writeCaseClassToCsv(Path("books.csv")))
.compile
.drain *> IO.println("Finished writing books to books.csv.")
}
```

@:@



[fs2]: https://fs2.io/#/
[fs2-data-csv]: https://fs2-data.gnieh.org/documentation/csv/
[decline]: https://ben.kirw.in/decline/
Expand Down