Click here to Skip to main content
15,113,245 members
Articles / Programming Languages / C#
Tip/Trick
Posted 26 Jan 2021

Tagged as

Stats

3.7K views
142 downloads
11 bookmarked

TABLEPLAY: A C# String-based Table Writer for Console or listview

Rate me:
Please Sign up or sign in to vote.
4.53/5 (6 votes)
26 Jan 2021CPOL2 min read
Simple class to write an array as a formatted, attractive table to console or other UI text display control
In this tip, you will find a simple class for writing an array as a formatted, attractive table (title, row and column headers, data-aligned, etc.) to the 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. DataGridViews 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 strings 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 strings). 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.

C#
// 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.

C#
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: 

History

  • 26th January, 2021: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

asiwel
CEO Academic Software, Inc.
United States United States
My public profile: http://www.linkedin.com/in/warrenlacefield
My research profile: https://www.researchgate.net/profile/Warren-Lacefield
My company homepage: http://www.acsw.com

Comments and Discussions

 
GeneralMy vote of 5 Pin
Hyland Computer Systems4-May-21 7:11
MemberHyland Computer Systems4-May-21 7:11 
GeneralRe: My vote of 5 Pin
asiwel30-May-21 7:28
professionalasiwel30-May-21 7:28 
QuestionA C# String-based Table Writer for Console or listview Pin
sainivedant4127-Jan-21 4:18
Membersainivedant4127-Jan-21 4:18 
AnswerRe: A C# String-based Table Writer for Console or listview Pin
asiwel27-Jan-21 7:20
professionalasiwel27-Jan-21 7:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.