oblivion
oblivion

Reputation: 6548

How to write a toCSV method for scala case class that would generate the csv string for the class?

I have a scala case class that is something like below.

case class Address(
              city: Option[String] = None,
              country: Option[String] = None
                )
case class Student(
              id: Option[String] = None,
              name: Option[String] = None,
              address: Option[Seq[Address]] = None,
              phone: Option[Seq[String]] = None
                )

Now I want to write a toCSV method for student that would generate a list of csv strings/lines for each student. I am unable to determine how to generate string format for fields that can have multiple values such as: address and phone.

For a student,

val student_1 = Student(
              id = Some("1"),
              name = Some("john"),
              address = Some(Seq(
                         Address(Some("Newyork"),Some("USA")),
                         Address(Some("Berlin"),Some("Germany")),
                         Address(Some("Tokyo"),Some("Japan")),
                         )),
              phone = Some(Seq(
                         "1111","9999","8888"
                         ))
              )

So, student_1.toCSV must result in following csv string:

id, name, address.city, address.country, phone
1 , John, Newyork     , USA            , 1111/9999/888
  ,     , Berlin      , Germany        , 
  ,     , Tokyo       , Japan          ,           

This is the csv list of string where first string represents the first row and so on.I need to generate this list of strings for each student. Note that there could be multiple lines for each student because address and phones can have multiple values.In this case, there are 3 addresses and 2 phones for student John. How do I achieve this in scala ?

Addition:

So far, I am working to produce a List of csv lines i.e a list of Lists where each list would store one row. So, the list would look like below:

List(
    List("id","name","address.city","address.country","phone"),
    List("1" ,"John","Newyork"     ,"USA"            ,"11111/22222"),
    List(""  ,""    ,"Berlin"      ,"Germany"        ,""),
    List(""  ,""    ,"Tokyo"       ,"Japan"          ,"")
    )

Upvotes: 1

Views: 387

Answers (2)

oblivion
oblivion

Reputation: 6548

Based on the answer provided by @jwvh above,

I have come up with a solution which is working for me now:

val student_1 = Student(
id = Some("1"),
name = Some("john"),
address = Some(Seq(
  Address(Some("Newyork"),Some("USA")),
  Address(Some("Berlin"),Some("Germany")),
  Address(Some("Tokyo"),Some("Japan"))
)),
phone = Some(Seq(
  "1111","9999","8888"
))
)

 def csvHeaders:List[String] = {
    List("StudentId","Name","Address.City","Address.Province","Phones")
 }

 def toCSV:List[List[String]] ={
   val maximumLength = address.getOrElse(Seq.empty[Address]).length max    1 
  //phone.getOrElse(Seq.empty[String]).length for earlier case where phones were kept in separate rows , done by @jwvh above
   val idList = List.tabulate(maximumLength)(k => "    ").updated(0,id.getOrElse(""))
   val nameList = List.tabulate(maximumLength)(k => "    ").updated(0,name.getOrElse(""))
   val addressCityList = if(address.isDefined){
    address.get.map{
    k => k.city.getOrElse(" ")
    }.toList.padTo(maximumLength," ")
    } else{
    List.tabulate(maximumLength)(k => " ")
    }
    val addressProvinceList = if(address.isDefined){
    address.get.map{
    k => k.province.getOrElse(" ")
    }.toList.padTo(maximumLength," ")
    } else{
    List.tabulate(maximumLength)(k => " ")
    }
    val phoneList = if(phone.isDefined){
    List.tabulate(maximumLength)(k => "     ").updated(0,phone.get.padTo(maximumLength," ").mkString("/"))
    } else{
    List.tabulate(maximumLength)(k => " ")
    }
    val transposedList:List[List[String]] =     List(idList,nameList,addressCityList,addressProvinceList,phoneList).transpose

    transposedList.+:(csvHeaders)
 }

So, now student_1.toCSV will return:

 /*   List(
        List(StudentId, Name, Address.City, Address.Province,      Phones),      
        List(1, john, Newyork, USA, 1111/9999/8888), 
        List( ,  , Berlin, Germany,  ), 
        List( ,  , Tokyo, Japan,  )
       ) */

Upvotes: 0

jwvh
jwvh

Reputation: 51271

In this I simplified your Address type to just a String, and I kept the phone layout as you had it originally (i.e. the one I complained about in the comments). So this is more of a proof-of-concept rather than a finished product.

val student = Student(Some("1")
  , Some("John")
  , Some(Seq("Newyork", "Berlin", "Tokyo"))
  , Some(Seq("1111","9999"))
)

student match {
  case Student(i,n,a,p) => 
    val maxLen = a.getOrElse(Seq("")).length max p.getOrElse(Seq("")).length
    Seq( Seq(i.getOrElse("")).padTo(maxLen,"")
       , Seq(n.getOrElse("")).padTo(maxLen,"")
       , a.getOrElse(Seq()).padTo(maxLen,"")
       , p.getOrElse(Seq()).padTo(maxLen,"")
       ).transpose
}
// res0: Seq[Seq[String]] = List( List(1, John, Newyork, 1111)
//                              , List(, , Berlin, 9999)
//                              , List(, , Tokyo, ))

Upvotes: 1

Related Questions