JohnBigs
JohnBigs

Reputation: 2811

How to flatten a case class with a list value to another case class properly with scala

I have case classes of Contact and Person:

case class Contact(id: String, name: String)
case class Person(id: String, name: String, age: Int, contacts: List[Contact])

lets say I have list of Person:

val pesonList = List(
  Person(1, "john", 30, List(Contact(5,"mark"),Contact(6,"tamy"),Contact(7,"mary"))),
  Person(2, "jeff", 40, List(Contact(8,"lary"),Contact(9,"gary"),Contact(10,"sam")))
)

I need to flatten this pesonList and transform it to list of:

case class FlattenPerson(personId: String, contactId: Option[String], personName: String)

so the results would be:

val flattenPersonList = List(
  FlattenPerson(1,"john"),
  FlattenPerson(1,5,"mark"),
  FlattenPerson(1,6,"tamy"),
  FlattenPerson(1, 7"mary"),
  FlattenPerson(2,"jeff"),
  FlattenPerson(2,8,"lary"),
  FlattenPerson(2,9,"gary"),
  FlattenPerson(2,10,"sam")
)

I found one way that looks like its working but dosent seem like the right way...it might break and scala probably have a more efficient way.

this is what I could come up with:

val people = pesonList.map(person => {
  FlattenPerson(person.id, None, person.name)
})

val contacts = pesonList.flatMap(person => {
  person.contacts.map(contact => {
  FlattenPerson(person.id, Some(contact.id), contact.name)
  })
})

val res = people ++ contacts

this would also have bad performance, I need to do it for each api call my app gets and it can be allot of calls plus i need to filter res.

would love to get some help here

Upvotes: 1

Views: 704

Answers (2)

Tim
Tim

Reputation: 27421

For reference here is a recursive versions of this algorithm that includes filtering in a single pass. This appears to perform somewhat faster than calling .filter(f) on the result. The non-filtered recursive version has no real performance advantage.

def flattenPeople(people: List[Person], f: FlattenPerson => Boolean): List[FlattenPerson] = {
  @annotation.tailrec
  def loop(person: Person, contacts: List[Contact], people: List[Person], res: List[FlattenPerson]): List[FlattenPerson] =
    contacts match {
      case Contact(id, name) :: tail =>
        val newPerson = FlattenPerson(person.id, Some(id), name)
        if (f(newPerson)) {
          loop(person, tail, people, newPerson +: res)
        } else {
          loop(person, tail, people, res)
        }
      case _ =>
        val newPerson = FlattenPerson(person.id, None, person.name)
        val newRes =  if (f(newPerson)) newPerson +: res else res
        people match {
          case p :: tail =>
            loop(p, p.contacts, tail, newRes)
          case Nil =>
            newRes.reverse
        }
    }

  people match {
    case p :: tail => loop(p, p.contacts, tail, Nil)
    case _ => Nil
  }
}

Upvotes: 0

jwvh
jwvh

Reputation: 51271

I think flatMap() can do what you're after.

personList.flatMap{pson =>
  FlattenPerson(pson.id, None, pson.name) :: 
    pson.contacts.map(cntc => FlattenPerson(pson.id, Some(cntc.id), cntc.name))
}
//res0: List[FlattenPerson] = List(FlattenPerson(1,None,john)
//                               , FlattenPerson(1,Some(5),mark)
//                               , FlattenPerson(1,Some(6),tamy)
//                               , FlattenPerson(1,Some(7),mary)
//                               , FlattenPerson(2,None,jeff)
//                               , FlattenPerson(2,Some(8),lary)
//                               , FlattenPerson(2,Some(9),gary)
//                               , FlattenPerson(2,Some(10),sam))

Upvotes: 3

Related Questions