diff --git a/docs/examples.md b/docs/examples.md index 427772f..90a4c5e 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -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: @@ -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 @@ -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 @@ -420,6 +420,107 @@ 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: + +```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)) +``` + +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/