2011-09-24 17 views
11

Necesito mostrar una tabla en una consola.Scala: Dibuje la tabla en la consola

Mi solución simple, si lo llamaría una "solución", es el siguiente:

override def toString() = { 
    var res = "\n" 
     var counter = 1; 
     res += stateDb._1 + "\n" 
     res += " +----------------------------+\n" 
     res += " +  State Table   +\n" 
     res += " +----------------------------+\n" 
     for (entry <- stateDb._2) { 
     res += " | " + counter + "\t | " + entry._1 + " | " + entry._2 + " |\n" 
     counter += 1; 
     } 
     res += " +----------------------------+\n" 
     res += "\n" 
    res 

    } 

No tenemos que discutir esta

  • una está mirando mal cuando se muestra
  • código b parece un poco en mal estado

en realidad, esta pregunta se le preguntó para C#, pero me gustaría para conocer una buena solución para Scala también.

Entonces, ¿qué es una forma (bonita/buena/simple/lo que sea) para dibujar una tabla en Scala a la consola?

------------------------------------------------------------------------- 
| Column 1  | Column 2  | Column 3  | Column 4  | 
------------------------------------------------------------------------- 
|     |     |     |     | 
|     |     |     |     | 
|     |     |     |     | 
------------------------------------------------------------------------- 

Respuesta

24

He sacado lo siguiente de mi proyecto actual:

object Tabulator { 
    def format(table: Seq[Seq[Any]]) = table match { 
    case Seq() => "" 
    case _ => 
     val sizes = for (row <- table) yield (for (cell <- row) yield if (cell == null) 0 else cell.toString.length) 
     val colSizes = for (col <- sizes.transpose) yield col.max 
     val rows = for (row <- table) yield formatRow(row, colSizes) 
     formatRows(rowSeparator(colSizes), rows) 
    } 

    def formatRows(rowSeparator: String, rows: Seq[String]): String = (
    rowSeparator :: 
    rows.head :: 
    rowSeparator :: 
    rows.tail.toList ::: 
    rowSeparator :: 
    List()).mkString("\n") 

    def formatRow(row: Seq[Any], colSizes: Seq[Int]) = { 
    val cells = (for ((item, size) <- row.zip(colSizes)) yield if (size == 0) "" else ("%" + size + "s").format(item)) 
    cells.mkString("|", "|", "|") 
    } 

    def rowSeparator(colSizes: Seq[Int]) = colSizes map { "-" * _ } mkString("+", "+", "+") 
} 

scala> Tabulator.format(List(List("head1", "head2", "head3"), List("one", "two", "three"), List("four", "five", "six"))) 
res1: java.lang.String = 
+-----+-----+-----+ 
|head1|head2|head3| 
+-----+-----+-----+ 
| one| two|three| 
| four| five| six| 
+-----+-----+-----+ 
+0

esto es hermoso. Lo intenté y era exactamente lo que estaba buscando. – evildead

+4

como una adición esto es alineación izquierda ("%" + tamaño + "s"). Formato (elemento) este derecho ("% -" + size + "s"). Formato (elemento) – evildead

+1

Sería incluso mejor que 'clase implícita' que agrega, por ejemplo '.asTable' a, p. 'Seq [Seq [Any]]' :) –

2

Tokenize it. Me gustaría empezar con el examen de lo que algunos objetos de casos y clases para que usted produce una lista de tokens que puede ser operado en fines de exhibición:

sealed trait TableTokens{ 
    val width: Int 
} 
case class Entry(value: String) extends TableTokens{ 
    val width = value.length 
} 
case object LineBreak extends TableTokens{ 
    val width = 0 
} 
case object Div extends TableTokens{ 
    val width = 1 
} 

Así entonces se puede formar ciertas limitaciones con algún tipo de objeto de fila :

case class Row(contents: List[TableTokens]) extends TableTokens{ 
    val width = contents.foldLeft(0)((x,y) => x = y.width) 
} 

Luego puede verificar restricciones y cosas así de una manera inmutable. Tal vez la creación de métodos para las tablas anexas y la alineación ...

case class Table(contents: List[TableTokens]) 

Eso significa que usted podría tener varias variantes diferentes de tablas en las que su estilo es diferente de su estructura, al estilo de HTML y CSS.

+0

Tal vez pueda agregar un pequeño ejemplo. No lo estoy entendiendo del todo. – evildead

+0

I second @ evildead: ¿por qué tokenizar algo si se trata de representar una secuencia existente? –

2

tonelada de gracias por el código tabulador!

Hay una modificación para Spark conjunto de datos de impresión en tabla.

que significa que usted puede imprimir el contenido trama de datos o conjunto de resultados tirado, como

Tabulator(hiveContext.sql("SELECT * FROM stat")) 
Tabulator(hiveContext.sql("SELECT * FROM stat").take(20)) 

La segunda será sin cabecera, por supuesto, para la implementación DF se puede establecer el número de filas de arrancar de las tramas de datos de chispa para impresión y necesitas encabezado o no.

/** 
* Tabular representation of Spark dataset. 
* Usage: 
* 1. Import source to spark-shell: 
* spark-shell.cmd --master local[2] --packages com.databricks:spark-csv_2.10:1.3.0 -i /path/to/Tabulator.scala 
* 2. Tabulator usage: 
* import org.apache.spark.sql.hive.HiveContext 
* val hiveContext = new HiveContext(sc) 
* val stat = hiveContext.read.format("com.databricks.spark.csv").option("header", "true").option("inferSchema", "true").option("delimiter", "\t").load("D:\\data\\stats-belablotski.tsv") 
* stat.registerTempTable("stat") 
* Tabulator(hiveContext.sql("SELECT * FROM stat").take(20)) 
* Tabulator(hiveContext.sql("SELECT * FROM stat")) 
*/ 
object Tabulator { 

    def format(table: Seq[Seq[Any]], isHeaderNeeded: Boolean) : String = table match { 
    case Seq() => "" 
    case _ => 
     val sizes = for (row <- table) yield (for (cell <- row) yield if (cell == null) 0 else cell.toString.length) 
     val colSizes = for (col <- sizes.transpose) yield col.max 
     val rows = for (row <- table) yield formatRow(row, colSizes) 
     formatRows(rowSeparator(colSizes), rows, isHeaderNeeded) 
    } 

    def formatRes(table: Array[org.apache.spark.sql.Row]): String = { 
    val res: Seq[Seq[Any]] = (for { r <- table } yield r.toSeq).toSeq 
    format(res, false) 
    } 

    def formatDf(df: org.apache.spark.sql.DataFrame, n: Int = 20, isHeaderNeeded: Boolean = true): String = { 
    val res: Seq[Seq[Any]] = (for { r <- df.take(n) } yield r.toSeq).toSeq 
    format(List(df.schema.map(_.name).toSeq) ++ res, isHeaderNeeded) 
    } 

    def apply(table: Array[org.apache.spark.sql.Row]): Unit = 
    println(formatRes(table)) 

    /** 
    * Print DataFrame in a formatted manner. 
    * @param df Data frame 
    * @param n How many row to take for tabular printing 
    */ 
    def apply(df: org.apache.spark.sql.DataFrame, n: Int = 20, isHeaderNeeded: Boolean = true): Unit = 
    println(formatDf(df, n, isHeaderNeeded)) 

    def formatRows(rowSeparator: String, rows: Seq[String], isHeaderNeeded: Boolean): String = (
    rowSeparator :: 
    (rows.head + { if (isHeaderNeeded) "\n" + rowSeparator else "" }) :: 
    rows.tail.toList ::: 
    rowSeparator :: 
    List()).mkString("\n") 

    def formatRow(row: Seq[Any], colSizes: Seq[Int]) = { 
    val cells = (for ((item, size) <- row.zip(colSizes)) yield if (size == 0) "" else ("%" + size + "s").format(item)) 
    cells.mkString("|", "|", "|") 
    } 

    def rowSeparator(colSizes: Seq[Int]) = colSizes map { "-" * _ } mkString("+", "+", "+") 

} 
Cuestiones relacionadas