0

Nobel Prize to anyone who can help me figure out if I'm doing this totally wrong. So what I'm trying to do is get an array of arrays of strings to print out as a table, e.g.

{ {"abcdefg", "hij"}, {"klm", "nopqrstuvwxyz"} }

----------------->

 ====================
  abcdefg | hij
 --------------------
  klm     | nopqrstu
          | vwxyz
 ====================

where there's a constant determining the width of the table (in characters).

So my code is a complete monstrosity and doesn't work:

    public static void PrintTable ( string[][] cells )
    {
        // Outputs content in cells matrix like 

        // =========================================
        //  cells[0][0]   | cells[0][1]
        // -----------------------------------------
        //  cells[1][0]   | cells[1][1]
        // -----------------------------------------
        //  cells[2][0]   | cells[2][1]
        //                .
        //                .
        // -----------------------------------------
        //  cells[n-1][0] | cells[n-1][1]
        //  ========================================

        // Each cell must be able to hold at least 1 character inside 
        // 1 space of padding on the left and right

        int linelen = OutputFormatter.OutputMaxLength; // copy of width (in characters) of lines being formatted
        // Calculate widths of columns 1 and 2 (excluding the single space of padding on the 
        // left and right side of each):
        int w1 = Math.Max(cells.Max(c => c[0].Length), linelen - 5);
        int w2 = linelen - w1 - 1;
        OutputFormatter.PrintChars('='); // prints a line of equals signs for the top border of the table

        // print out rows:
        foreach ( string[] row in cells )
        {
            // Make the strings corresponding to the first and second column have equal
            // length by adding whitespace to the end of the shorter one:
            int m1 = row[0].Length, m2 = row[1].Length;
            String padding = new String(' ', Math.Abs(m1 - m2));
            if ( m1 > m2 ) row[1] += padding;
            else row[0] += padding;

            // Print out content of row
            for ( int i = 0, j = 0, n = row[0].Length; i < n && j < n; i += w1, j += w2 )
            {
                Console.WriteLine(" {0} | {1} ",
                                    row[0].Substring(i,w1),
                                    row[1].Substring(j,w2));
            }
            OutputFormatter.PrintChars('-'); // prints a line of dashes to separate rows
        }

        OutputFormatter.PrintChars('='); // prints a line of equals signs to form bottom border of table
    }

Anyone have a 1-line solution for this? ;)

I've stepped through debugging and the exception is getting thrown after hitting

                Console.WriteLine(" {0} | {1} ",
                                    row[0].Substring(i,w1),
                                    row[1].Substring(j,w2));

when I test with the input string

{
   new string[] {"CompareLastTwo","Shows difference between friends lists of last two Facebook data files in repository"},
   new string[] {"AddFriendList <DataFolderPath>", "blah blah blah"}
};

and all that has printed is

enter image description here

Can someone help me figure out the logical error(s) I am making here? And is there a way I can better leverage the .NET library to make this elegant, compact, efficient, clever and readable?

user5572578
  • 159
  • 6
  • Hey, I remember that OutputFormatter.OutputMaxLength from somewhere :) – Oguz Ozgul Nov 24 '15 at 06:50
  • @OguzOzgul All that matters is that it's a constant. – user5572578 Nov 24 '15 at 06:52
  • Can you explain your goal a bit more? The values in your input array doesn't entirely match your example output. Where has `klm` gone? And the `nopqrstuvwxyz` value - that's split in two? –  Nov 24 '15 at 06:52
  • @SimonKittle Sorry, there was a type-o. It should make sense now. – user5572578 Nov 24 '15 at 06:53
  • What exception is being thrown? –  Nov 24 '15 at 06:58
  • I would suggest that the error with the above code is that you've normalised the string length - the length of each of the columns - but then in the loop where you're printing them out, you're incrementing the starting index for the call to `Substring` by different amounts (that is, don't `i` and `j` need to be incremented by same amount given the normalisation?) –  Nov 24 '15 at 07:05
  • 1
    related: [How to efficiently find the ideal column count for strings of a certain width?](http://stackoverflow.com/questions/24411759/how-to-efficiently-find-the-ideal-column-count-for-strings-of-a-certain-width) – greybeard Nov 24 '15 at 08:09

2 Answers2

1

First of all, it is better to keep the formatting logic in the OutputFormatter class instead of just using a constant value from it.

The following code should work for you.

public class OutputFormatter
{
    public const int OutputMaxLength = 16;

    public static string[] Format(string output)
    {
        int offset = 0;
        List<string> outputParsed = new List<string>();
        while (offset < output.Length)
        {
            outputParsed.Add(output.Substring(offset, Math.Min(OutputMaxLength, output.Length - offset)));
            offset += OutputMaxLength;
        }
        return outputParsed.ToArray();
    }
}

private static string[][] strings = { 
        new string[] {"CompareLastTwo","Shows difference between friends lists of last two Facebook data files in repository"},
        new string[] {"AddFriendList <DataFolderPath>", "blah blah blah"}
    };

public static void Main(string[] args)
{
    foreach (string[] pair in strings)
    {
        string[] value0 = OutputFormatter.Format(pair[0]);
        string[] value1 = OutputFormatter.Format(pair[1]);

        int maxRows = Math.Max(value0.Length, value1.Length);
        string template = "{0," + OutputFormatter.OutputMaxLength + "} | {1," + OutputFormatter.OutputMaxLength + "}";
        for (int row = 0; row < maxRows; row++)
        {
            Console.Write
            (
                template,
                value0.Length > row ? value0[row] : null,
                value1.Length > row ? value1[row] : null
            );
            Console.WriteLine();
        }
        Console.WriteLine(new string('-', OutputFormatter.OutputMaxLength * 2));
    }
}
Oguz Ozgul
  • 6,809
  • 1
  • 14
  • 26
0

Here's a slightly different approach, which allows for differing column widths:

class Program
{
    static void Main(string[] args)
    {
        string[][] sampleInput = {
            new string[] { "Column 1 Head", "Column 2 Header" },
            new string[] { "klm", "nopqrstuvwxyz" },
            new string[] { "klm dksfj sldkf sdlk", "nalsdk als dkasopqrstuvwxyz" }
        };

        PrintJaggedArrayWithWrappedLines(sampleInput, 15, 12);

        Console.ReadLine();
    }

    public static void PrintJaggedArrayWithWrappedLines(string[][] jaggedArray, int leftColumnMaxWidth, int rightColumnMaxWidth)
    {
        for (int j = 0; j < jaggedArray.Length; j++)
        {
            string[] aRow = jaggedArray[j];
            int leftColumnNumLines = (int)Math.Ceiling((double)aRow[0].Length / (double)leftColumnMaxWidth);
            int rightColumnNumLines = (int)Math.Ceiling((double)aRow[1].Length / (double)rightColumnMaxWidth);
            int numberLines = Math.Max(leftColumnNumLines, rightColumnNumLines);

            string leftColumn = aRow[0].PadRight(numberLines * leftColumnMaxWidth, ' ');
            string rightColumn = aRow[1].PadRight(numberLines * rightColumnMaxWidth, ' ');

            for (int i = 0; i < numberLines; i++)
            {
                Console.WriteLine(" {0} | {1} ", leftColumn.Substring(i * leftColumnMaxWidth, leftColumnMaxWidth), rightColumn.Substring(i * rightColumnMaxWidth, rightColumnMaxWidth));
            }
            if (j == 0)
            {
                Console.WriteLine(new String('-', leftColumnMaxWidth + rightColumnMaxWidth + 4));
            }
        }
    }
}

On the sample input, the result is:

 Column 1 Head   | Column 2 Hea
                 | der
-------------------------------
 klm             | nopqrstuvwxy
                 | z
 klm dksfj sldkf | nalsdk als d
  sdlk           | kasopqrstuvw
                 | xyz
  • You have `if(j==0)` inside a loop where it is guaranteed that the `if` statement will evaluate to `false` exactly once. That's shoddy. – user5572578 Nov 24 '15 at 14:15
  • You mean it will evaluate to `true` exactly once, not `false`. –  Nov 24 '15 at 15:31