TABLEPLAY: A C# String-based Table Writer for Console or listview
Simple class to write an array as a formatted, attractive table to console or other UI text display control
Introduction
I program mostly in C# on an iMac with a Windows virtualbox
, doing a good bit of what might be called “one-off” scientific computing and typically using simple Winforms or the console as my UI (mostly console on the Mac side – Xamarin Mac apps are great but sometimes the UI programming overhead is more work than the program itself). During development, debugging, and experimenting, I typically need to view tables of data objects quickly and easily and so am constantly writing and rewriting custom Console.WriteLine
loops, etc. DataGridView
s and similar tools also are sometimes tedious to work with. Finally, I decided to write a custom “Table
” writer. Perhaps you have done that too.
I wanted a routine that I could just drop an array into and receive back a nice output string. A routine that worked with all sorts of arrays and collections, could be modified or expanded easily, offered nice row and column headers, maybe a title, and neatly aligned data elements. Originally, I was mostly concerned with numerical data tables, so the basic table element was a formatted double
. But since the output was to be a “stringbuilt” string
anyway, here I decided to use string
s as the basic element to begin with. And to tolerate three copies in memory (original array, string
array, and stringbuilt string
) for a moment or two. After all, the table
objects I want to print are seldom enormous; memory is seldom a problem; and the objects are disposed after one time use, etc.
Here is the code for my Table
object generator. Nothing special and surely improvable. This should be easy to modify for custom purposes. One note is that column widths are determined by the “widest” value in each column. String
values are then aligned Left
, Center
or Right
within those widths. The download provides examples of other type constructors and special writers (e.g., for CSV string
s). If you are using a ListView
control of some kind or, eventually, a text document, this table writer is designed for a monospaced font. I hope readers might find this a handy and useful addition to their personal code libraries.
// TABLEPLAY: A string-based table writer demonstration for CodeProject
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
namespace TablePlay
{
public class Table
{
private string title;
private readonly int cols;
private readonly int rows;
private readonly int colSpace;
private string colSpaceStr;
private int[] maxColWidths;
private CultureInfo culture;
private string numFormat;
private string[] colHdrs;
private string[] rowHdrs;
private readonly string[,] array;
private readonly StringBuilder sb = new StringBuilder();
#region CONSTRUCTORS
public Table(int[] ar, int columnSpace = 1, bool prtincol = false)
{ .... }
public Table(int[,] ar, int columnSpace = 1)
{ .... }
public Table(double[,] ar, int dec = 3, int columnSpace = 1)
{
culture = CultureInfo.CreateSpecificCulture("en-US");
numFormat = "F" + dec.ToString();
colSpace = columnSpace;
rows = ar.GetLength(0);
cols = ar.GetLength(1);
array = new string[rows, cols];
for (int i = 0; i < rows; i++)
for (int j = 0; j < cols; j++)
array[i, j] = ar[i, j].ToString(numFormat, culture);
Initialize();
}
public Table(String[,] ar, int columnSpace = 1)
{ .... }
private void Initialize()
{
title = "";
maxColWidths = new int[cols + 1];
colSpaceStr = new string(' ', colSpace);
colHdrs = new string[cols + 1];
rowHdrs = new string[rows];
Array.Fill(colHdrs, ""); // Initialize hdrs with empty strings
Array.Fill(rowHdrs, "");
for (int j = 0; j < cols; j++) // Initialize maxColWidths appropriately
for (int i = 0; i < rows; i++)
{
int len = array[i, j].Length;
if (maxColWidths[j + 1] < len) maxColWidths[j + 1] = len;
}
sb.Clear(); // Clear the Stringbuilder
}
#endregion
#region METHODS
public string Title
{
get { return title; }
set { title = value; }
}
public string[] ColHdrs
{
get {return colHdrs;}
set
{
int n = value.Length > cols + 1 ? cols + 1 : value.Length;
for (int j = 0; j < n; j++) if (value[j] is not null) colHdrs[j] = value[j];
}
}
public string[] RowHdrs
{ .... }
public string GetTable()
{
string s = "";
// First, insure room for table label in upper left corner)
maxColWidths[0] = colHdrs[0].Length;
for (int i = 0; i < rows; i++) // Then check row headers
{
if (maxColWidths[0] < rowHdrs[i].Length) maxColWidths[0] = rowHdrs[i].Length;
}
s += StringUtils.Left(colHdrs[0], maxColWidths[0]); // align table header left
// Second, check space for other column headers and center them
for (int j = 1; j < cols + 1; j++)
{
if (maxColWidths[j] < colHdrs[j].Length) maxColWidths[j] = colHdrs[j].Length;
s += colSpaceStr + StringUtils.Center(colHdrs[j], maxColWidths[j]);
}
// Center and add any title. At this point, s.Length is total width of table
if (title is not "") sb.Append(StringUtils.Center(title, s.Length) + "\n");
// If there is any column header text at all, add them and an underline bar
bool colHdrFlag = false;
for (int j = 0; j < cols + 1; j++) if (colHdrs[j] != "") colHdrFlag = true;
if (colHdrFlag)
{
sb.Append(s + "\n");
sb.Append(new string
('\u2015', maxColWidths.Sum() + cols * colSpace) + "\n");
}
for (int i = 0; i < rows; i++)
{
// Left justify each row header
s = $"{StringUtils.Left(rowHdrs[i], maxColWidths[0])}";
// Add column spacing and right justify column data
for (int j = 0; j < cols; j++)
s += colSpaceStr + StringUtils.Right(array[i, j], maxColWidths[j + 1]);
sb.Append(s + "\n");
}
return sb.ToString();
}
#endregion
public class StringUtils
{
// Many versions of these can be found on StackOverFlow
public static string Left(string s, int size, char pad = ' ')
{
try { if (size <= s.Length) return s; }
catch { return s; }
StringBuilder sb = new StringBuilder(size);
sb.Append(s);
while (sb.Length < size) sb.Append(pad);
return sb.ToString();
}
public static string Right(string s, int size, char pad = ' ')
{ .... }
public static string Center(string s, int size, char pad = ' ')
{ .... }
// Example of a custom table writer
public static string CSVWriter(List<string> csvstrings,
int columnSpace = 1, string title = "")
{ .... }
}
}
}
Using the Code
This table writer works well with Visual Studio for Windows and Visual Studio for Mac. Here is a sample of code to call the table writer and display output.
double[,] Rxy = new double[3, 3] {
{ 1.0, -.25, 0.123 },
{ -.25, 1.0, .300 },
{ 0.123, 0.300, 1.0 } };
// double[,] table, col and row hdrs, title
Table myTable1 = new Table(Rxy) {
Title = "TABLE 1: Correlation Matrix",
ColHdrs = new[] { "Rxy", "C1", "C2", "C3" },
RowHdrs = new[] { "C1", "C2", "C3" } };
Console.WriteLine(myTable1.GetTable());
There are many interesting articles on formatting tables on CodeProject and other sources. For example, check out:
- Print DataTable to Console (and more) (2017, by Yuvasol)
- Fun Exploring Div and Table Layout (2019, by Marc Clifton)
History
- 26th January, 2021: Initial version