2

I was trying to recursively create a key, value pair map from a hierarchical case class structure, but no luck.

case class Country(id: Option[Long], name: String, states: Option[List[State]])
case class State(id: Option[Long], name: String, cities: Option[List[City]])
case class City(id: Option[Long], name: String)

So I was trying to extract that in Lists and zip to get the key value pair, then trying to do some recursive computation to obtain the same in the Lists of objects.

      val keys = country.getClass.getDeclaredFields.map(f => f.getName)
      val values = country.productIterator    
      val m = (keys zip values.toList).toMap

This gives me 3 keys with their values.

My problem is to solve the recursive way in the List[City] inside State and List[State] inside Country.

Is there any good way to do that? I can't think in any good solution. I was trying to iterate over the map, matching where there is a List and iterate and trying to save this progress in a BufferList.

Has anyone trying to do something like this?

EDIT

I wasn't specific about the desirable output.

I want the child to be other maps, so getting the example from @Tienan Ren I tried to do something like this:

def toMap[T: TypeTag](clazz: scala.Product): Map[String, Any] = {

    def recursion(key: String, list: List[T]): Map[String, Any] = {
      list.toMap
    }

    val keys = clazz.getClass.getDeclaredFields.map(_.getName)

    val values = (keys zip clazz.productIterator.toList) map {
      case (key, value: List[T]) => recursion(key, value)
      case (key, value) => (key, value)
    }

    values.toMap
  }

Where the recursion should receive the list and back a Map.

4
  • 1
    What are you trying to do? Can you provide an example of your expected outcome? Commented May 14, 2015 at 4:26
  • Please note that getDeclaredFields is not guaranteed to return fields in any order. It is implementation-dependent and can be arbitrary and may not coincide with whatever productIterator returns. Commented May 14, 2015 at 11:42
  • @Justin Pihony I've just updated my questions, sorry. Commented May 14, 2015 at 22:02
  • @Wladimir Matveev that is going to be a problem since I want the exactly value to the respective key... any thoughts? Commented May 14, 2015 at 22:03

1 Answer 1

2

Not sure why would you use Option[List[..]], empty list should be capable of representing the None case. Here is my implementation after removing Option for Lists.

  case class Country(id: Option[Long], name: String, states: List[State])
  case class State(id: Option[Long], name: String, cities: List[City])
  case class City(id: Option[Long], name: String)

The toMap function:

 import reflect.runtime.universe._

 def toMap[T: TypeTag](c: scala.Product): Map[String, Any] = {

   val keys = c.getClass.getDeclaredFields.map(_.getName)

   val z = (keys zip c.productIterator.toList) map {
      case (key, value: List[T]) if typeOf[T] <:< typeOf[scala.Product] =>
        (key, value.map(v => toMap(v.asInstanceOf[scala.Product])))
      case (key, value) => (key, value)
   }

   z.toMap
}

Output:

val country = Country(None, "US", List(State(None, "CA", City(None, "LA") :: Nil)))

println(toMap(country))

This gives you:

Map(id -> None, name -> US, states -> List(Map(id -> None, name -> CA, cities -> List(Map(id -> None, name -> LA)))))
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.