Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pretty print a nested Map in scala

Tags:

scala

Suppose I have a nexted Map in Scala as below:

type MapType = Map[String, Map[String, Map[String, (String, String)]]]

val m: MapType = Map("Alphabet" -> Map( "Big Boss" -> Map("Clandestine Mssion" -> ("Dracula Returns", "Enemy at the Gates"))))

println(m)

This would output the Map as shown below:

Map(Alphabet -> Map(Big Boss -> Map(Clandestine Mssion -> (Dracula Returns,Enemy at the Gates))))

How can I print it like below instead?:

    Map(
      Alphabet -> Map(Big Boss -> Map(
               Clandestine Mssion -> (Dracula Returns,Enemy at the Gates)
                                     )
                      )
       )

Or in a way that is similar to pretty nested JSON.

like image 723
tuxdna Avatar asked Dec 04 '22 02:12

tuxdna


2 Answers

This should do the trick

object App {

  def main(args : Array[String]) {
    type MapType = Map[String, Map[String, Map[String, (String, String)]]]

    val m: MapType = Map("Alphabet" -> Map( "Big Boss" -> Map("Clandestine Mssion" -> ("Dracula Returns", "Enemy at the Gates"))))

    println(m.prettyPrint)
  }

  implicit class PrettyPrintMap[K, V](val map: Map[K, V]) {
    def prettyPrint: PrettyPrintMap[K, V] = this

    override def toString: String = {
      val valuesString = toStringLines.mkString("\n")

      "Map (\n" + valuesString + "\n)"
    }

    def toStringLines = {
      map
        .flatMap{ case (k, v) => keyValueToString(k, v)}
        .map(indentLine(_))
    }

    def keyValueToString(key: K, value: V): Iterable[String] = {
      value match {
        case v: Map[_, _] => Iterable(key + " -> Map (") ++ v.prettyPrint.toStringLines ++ Iterable(")")
        case x => Iterable(key + " -> " + x.toString)
      }
    }

    def indentLine(line: String): String = {
      "\t" + line
    }
  }
}

The output is

Map (
    Alphabet -> Map (
        Big Boss -> Map (
            Clandestine Mssion -> (Dracula Returns,Enemy at the Gates)
        )
    )
)
like image 105
Till Rohrmann Avatar answered Dec 22 '22 12:12

Till Rohrmann


Here's an approach using type classes that avoids casting and toString.

trait Show[A] {
  def apply(a: A): String
}

object Show {
  def apply[A](f: A => String): Show[A] =
    new Show[A] {
      def apply(a: A): String = f(a)
    }
}

implicit def showString: Show[String] = Show(identity)

implicit def show2[A, B](implicit sA: Show[A], sB: Show[B]): Show[(A, B)] =
  Show({ case (a, b) => s"(${sA(a)}, ${sB(b)})" })

implicit def showMap[A, B](implicit sA: Show[A], sB: Show[B]): Show[Map[A, B]] =
  Show(m => s"Map(\n${
    m.map({
      case (a, b) => s"    ${sA(a)} -> ${sB(b).replace("\n", "\n    ")}"
    }).mkString(",\n")
  }\n)")

def show[A](a: A)(implicit s: Show[A]): String = s(a)

val m = Map("Alphabet" -> Map("Big Boss" -> Map("Clandestine Mission" ->
  ("Dracula Returns", "Enemy at the Gates"))))

show(m)

Result:

Map(
    Alphabet -> Map(
        Big Boss -> Map(
            Clandestine Mission -> (Dracula Returns, Enemy at the Gates)
        )
    )
)
like image 45
Chris Martin Avatar answered Dec 22 '22 13:12

Chris Martin