@@ -10,7 +10,7 @@ This example was written by [Koroeskohr] and taken from the [Virtuslab Blog](htt
1010@: choice (scala-3)
1111
1212``` scala mdoc:silent
13- //> using lib " org. typelevel::toolkit:: @VERSION@"
13+ //> using toolkit typelevel:@VERSION@
1414
1515import cats .effect .*
1616import io .circe .Decoder
@@ -47,7 +47,7 @@ object Main extends IOApp.Simple:
4747@: choice (scala-2)
4848
4949``` scala mdoc:reset:silent
50- //> using lib " org. typelevel::toolkit:: @VERSION@"
50+ //> using toolkit typelevel:@VERSION@
5151
5252import cats .effect ._
5353import io .circe .Decoder
@@ -108,7 +108,7 @@ $ echo -e "foo\nbar" | ./mkString --prefix "[" -d "," --suffix "]"
108108
109109@: choice (scala-3)
110110``` scala mdoc:reset:silent
111- //> using lib " org. typelevel::toolkit:: @VERSION@"
111+ //> using toolkit typelevel:@VERSION@
112112
113113import cats .effect .*
114114import cats .syntax .all .*
@@ -136,7 +136,7 @@ object Main extends CommandIOApp("mkString", "Concatenates strings from stdin"):
136136@: choice (scala-2)
137137
138138``` scala mdoc:reset:silent
139- //> using lib " org. typelevel::toolkit:: @VERSION@"
139+ //> using toolkit typelevel:@VERSION@
140140
141141import cats .effect ._
142142import cats .syntax .all ._
@@ -166,7 +166,7 @@ object Main extends CommandIOApp("mkString", "Concatenates strings from stdin")
166166
167167## Parsing and transforming a CSV file
168168
169- Here, [ fs2-data-csv] is used to read and parse a comma separated file.
169+ Here, [ fs2-data-csv] is used to read and parse a comma separated file.
170170Manual encoders and decoders are defined for our ` Passenger ` s to show you how to do everything from scratch.
171171
172172Let's start with a CSV file that has records of fictious passengers registered for a flight:
@@ -183,7 +183,7 @@ id,First Name,Age,flight number,destination
183183
184184@: choice (scala-3)
185185``` scala mdoc:reset:silent
186- //> using lib " org. typelevel::toolkit:: @VERSION@"
186+ //> using toolkit typelevel:@VERSION@
187187
188188import cats .effect .*
189189import fs2 .text
@@ -233,7 +233,7 @@ val input = Files[IO]
233233
234234object CSVPrinter extends IOApp .Simple :
235235
236- /** First we'll do some logging for each row,
236+ /** First we'll do some logging for each row,
237237 * and then calculate and print the mean age */
238238 val run =
239239 input
@@ -254,7 +254,7 @@ object CSVPrinter extends IOApp.Simple:
254254
255255@: choice (scala-2)
256256``` scala mdoc:reset:silent
257- //> using lib " org. typelevel::toolkit:: @VERSION@"
257+ //> using toolkit typelevel:@VERSION@
258258
259259import cats .effect ._
260260import fs2 .text
@@ -310,7 +310,7 @@ object CSVPrinter extends IOApp.Simple {
310310 .through(decodeUsingHeaders[Passenger ]())
311311
312312
313- /** First we'll do some logging for each row,
313+ /** First we'll do some logging for each row,
314314 * and then calculate and print the mean age */
315315 val run =
316316 input
@@ -358,7 +358,7 @@ perft 6 227689589
358358@: choice (scala-3)
359359
360360``` scala mdoc:reset:silent
361- //> using lib " org. typelevel::toolkit:: @VERSION@"
361+ //> using toolkit typelevel:@VERSION@
362362
363363import cats .effect .{IO , IOApp }
364364import fs2 .{Stream , text }
@@ -388,7 +388,7 @@ object PerftConverter extends IOApp.Simple:
388388
389389@: choice (scala-2)
390390``` scala mdoc:reset:silent
391- //> using lib " org. typelevel::toolkit:: @VERSION@"
391+ //> using toolkit typelevel:@VERSION@
392392
393393import cats .effect .{IO , IOApp }
394394import fs2 .{Stream , text }
@@ -420,6 +420,127 @@ object PerftConverter extends IOApp.Simple {
420420```
421421@:@
422422
423+ ## Writing data to a CSV file
424+
425+ If you want to save a list of a case class into a CSV file this utility may aid you:
426+
427+ @: select (scala-version)
428+
429+ @: choice (scala-3)
430+ ``` scala
431+ // Define your case class and derive an encoder for it
432+ case class YourCaseClass (n : String , i : Int )
433+ given CsvRowEncoder [YourCaseClass , String ] = deriveCsvRowEncoder
434+
435+ // Writes a case class as a csv given a path.
436+ def writeCaseClassToCsv [A ](
437+ path : Path
438+ )(using CsvRowEncoder [A , String ]): Pipe [IO , A , Nothing ] =
439+ _.through(encodeUsingFirstHeaders(fullRows = true ))
440+ .through(fs2.text.utf8.encode)
441+ .through(Files [IO ].writeAll(path))
442+ ```
443+
444+ @: choice (scala-2)
445+ ``` scala
446+ case class YourCaseClass (n : String , i : Int )
447+ implicit val csvRowEncoder : CsvRowEncoder [YourCaseClass , String ] = deriveCsvRowEncoder
448+
449+ object Helpers {
450+ // Writes a case class as a csv given a path.
451+ def writeCaseClassToCsv [A ](
452+ path : Path
453+ )(implicit encoder : CsvRowEncoder [A , String ]): Pipe [IO , A , Nothing ] =
454+ _.through(encodeUsingFirstHeaders(fullRows = true ))
455+ .through(fs2.text.utf8.encode)
456+ .through(Files [IO ].writeAll(path))
457+ }
458+ ```
459+ @:@
460+
461+ As an example, let's imagine we have a ` Book ` class we would like to write to a ` .csv ` file.
462+
463+ @: select (scala-version)
464+
465+ @: choice (scala-3)
466+ ``` scala mdoc:reset:silent
467+ //> using toolkit typelevel:@VERSION@
468+
469+ import fs2 .data .csv .*
470+ import fs2 .data .csv .generic .semiauto .*
471+ import fs2 .io .file .{Files , Path }
472+ import cats .effect .{IO , IOApp }
473+ import fs2 .{Pipe , Stream }
474+
475+ def writeCaseClassToCsv [A ](
476+ path : Path
477+ )(using CsvRowEncoder [A , String ]): Pipe [IO , A , Nothing ] =
478+ _.through(encodeUsingFirstHeaders(fullRows = true ))
479+ .through(fs2.text.utf8.encode)
480+ .through(Files [IO ].writeAll(path))
481+
482+
483+ object WriteBooksToCsv extends IOApp .Simple :
484+ case class Book (id : Long , name : String , isbn : String )
485+ given CsvRowEncoder [Book , String ] = deriveCsvRowEncoder
486+
487+ val input = Seq (
488+ Book (1 , " Programming in Scala" , " 9780997148008" ),
489+ Book (2 , " Hands-on Scala Programming" , " 9798387677205" ),
490+ Book (3 , " Functional Programming in Scala" , " 9781617299582" )
491+ )
492+
493+ def run : IO [Unit ] =
494+ Stream
495+ .emits(input)
496+ .through(writeCaseClassToCsv(Path (" books.csv" )))
497+ .compile
498+ .drain *> IO .println(" Finished writing books to books.csv." )
499+ ```
500+
501+ @: choice (scala-2)
502+
503+ ``` scala mdoc:reset:silent
504+ //> using toolkit typelevel:@VERSION@
505+
506+ import fs2 .data .csv ._
507+ import fs2 .data .csv .generic .semiauto ._
508+ import fs2 .io .file .{Files , Path }
509+ import cats .effect .{IO , IOApp }
510+ import fs2 .{Pipe , Stream }
511+
512+ object Helpers {
513+ def writeCaseClassToCsv [A ](
514+ path : Path
515+ )(implicit encoder : CsvRowEncoder [A , String ]): Pipe [IO , A , Nothing ] =
516+ _.through(encodeUsingFirstHeaders(fullRows = true ))
517+ .through(fs2.text.utf8.encode)
518+ .through(Files [IO ].writeAll(path))
519+ }
520+
521+ object WriteBooksToCsv extends IOApp .Simple {
522+ case class Book (id : Long , name : String , isbn : String )
523+ implicit val csvRowEncoder : CsvRowEncoder [Book , String ] = deriveCsvRowEncoder
524+
525+ val input = Seq (
526+ Book (1 , " Programming in Scala" , " 9780997148008" ),
527+ Book (2 , " Hands-on Scala Programming" , " 9798387677205" ),
528+ Book (3 , " Functional Programming in Scala" , " 9781617299582" )
529+ )
530+
531+ def run : IO [Unit ] =
532+ Stream
533+ .emits(input)
534+ .through(Helpers .writeCaseClassToCsv(Path (" books.csv" )))
535+ .compile
536+ .drain *> IO .println(" Finished writing books to books.csv." )
537+ }
538+ ```
539+
540+ @:@
541+
542+
543+
423544[ fs2 ] : https://fs2.io/#/
424545[ fs2-data-csv ] : https://fs2-data.gnieh.org/documentation/csv/
425546[ decline ] : https://ben.kirw.in/decline/
0 commit comments