Type Inference
Scautable supports a bouqet of type inference strategies via it's TypeInferrer
enum. The varying strategies are discussed below.
Scautable targets "small". If you ask the compiler to infer the types of all rows in a 10 million row CSV file, strange things may begin to happen.
String Type
This is essentially "Safe Mode". it does nothing other than read the strings. It should fail, only if the CSV is poorly formed.
import io.github.quafadas.table.*
inline val csvContent = "Name,Age\nAlice,30\nBob,24\nJim,50"
// csvContent: String = """Name,Age
// Alice,30
// Bob,24
// Jim,50"""
val c: CsvIterator[("Name", "Age"), (String, String)] = CSV.fromString(csvContent, TypeInferrer.StringType)
// c: CsvIterator[Tuple2["Name", "Age"], Tuple2[String, String]] = empty iterator
c.foreach(println)
// (Alice,30)
// (Bob,24)
// (Jim,50)
FirstN
The compiler will read the firstN rows of the CSV file. It will test every cell, in every column, for the firstN rows, whether they can be decoded as
- Boolean
- Int
- Long
- Double
CSV.fromString(csvContent, TypeInferrer.FirstN(2))
// res1: CsvIterator[Tuple2["Name", "Age"], *:[String, *:[Int, EmptyTuple]]] = non-empty iterator
FirstRow
Is firstN, but with Rows set to be 1 🤷
CSV.fromString(csvContent, TypeInferrer.FirstRow)
// res2: CsvIterator[Tuple2["Name", "Age"], *:[String, *:[Int, EmptyTuple]]] = non-empty iterator
FromAllRows
Is firstN, but with Rows set to be Int.MaxValue
.
val tmp: CsvIterator[("Name", "Age"), (String, Int)]= CSV.fromString(csvContent, TypeInferrer.FromAllRows)
// tmp: CsvIterator[Tuple2["Name", "Age"], Tuple2[String, Int]] = empty iterator
tmp.foreach(println)
// (Alice,30)
// (Bob,24)
// (Jim,50)
FromTuple
You take "manual" control of the decoding process.
val csv1: CsvIterator[("Name", "Age"), (String, Option[Double])] = CSV.fromString(
csvContent,
TypeInferrer.FromTuple[(String, Option[Double])]()
)
// csv1: CsvIterator[Tuple2["Name", "Age"], Tuple2[String, Option[Double]]] = empty iterator
csv1.foreach(println)
// (Alice,Some(30.0))
// (Bob,Some(24.0))
// (Jim,Some(50.0))
Using this strategy, is it possible to decode the CSV to custom types.
import io.github.quafadas.scautable.Decoder
enum Status:
case Active, Inactive
inline given Decoder[Status] with
def decode(str: String): Option[Status] =
str match
case "Active" => Some(Status.Active)
case "Inactive" => Some(Status.Inactive)
case _ => None
val csv: CsvIterator[("name", "active", "status"), (String, Boolean, Status)] =
CSV.fromString("name,active,status\nAlice,true,Active\nBob,false,Inactive", TypeInferrer.FromTuple[(String, Boolean, Status)]())
// csv: CsvIterator[Tuple3["name", "active", "status"], Tuple3[String, Boolean, Status]] = empty iterator
csv.foreach(println)
// (Alice,true,Active)
// (Bob,false,Inactive)