Реализация преобразователя двумерных массивов в таблицу уценки на C #

Я пытаюсь реализовать конвертер, который может преобразовывать двумерный массив (например, string[,]) в таблица уценки.

Экспериментальная реализация

Экспериментальная реализация Converter класс, как показано ниже.

public static class Converter
{
    /// <summary>
    /// Align formats
    /// </summary>
    public enum Align
    {
        Right,
        Center,
        Left,
        Default
    }

    private static TOutput[,] ConvertAll<TInput, TOutput>(TInput[,] array, Converter<TInput, TOutput> converter)
    {
        //    null check
        if (ReferenceEquals(array, null))
        {
            throw new ArgumentNullException($"{nameof(array)} is null");
        }

        if (ReferenceEquals(converter, null))
        {
            throw new ArgumentNullException($"{nameof(converter)} is null");
        }

        var output = new TOutput[array.GetLongLength(0), array.GetLongLength(1)];
        for (long row = 0; row < array.GetLongLength(0); row++)
        {
            for (long column = 0; column < array.GetLongLength(1); column++)
            {
                output[row, column] = converter(array[row, column]);
            }
        }
        return output;
    }

    /// <summary>
    /// ToMarkdownTable method with TInput type array
    /// </summary>
    /// <param name="input">Input array</param>
    /// <param name="align">Align format of all columns</param>
    /// <returns></returns>
    public static string[] ToMarkdownTable<TInput>(TInput[,] input, Align align = Align.Default)
    {
        return ToMarkdownTable(ConvertAll(input, element => element.ToString()), align);
    }

    /// <summary>
    /// ToMarkdownTable method with TInput type array and the parameter for assigning align format of each column
    /// </summary>
    /// <param name="input">Input array</param>
    /// <param name="aligns">Align format of each column</param>
    /// <returns></returns>
    public static string[] ToMarkdownTable<TInput>(TInput[,] input, Align[] aligns)
    {
        return ToMarkdownTable(ConvertAll(input, element => element.ToString()), aligns);
    }


    /// <summary>
    /// ToMarkdownTable method
    /// </summary>
    /// <param name="input">Input array</param>
    /// <param name="align">Align format of all columns</param>
    /// <returns></returns>
    public static string[] ToMarkdownTable(string[,] input, Align align = Align.Default)
    {
        Align[] format = new Align[input.GetLongLength(1)];
        Array.Fill(format, align);
        return ToMarkdownTable(input, format);
    }

    /// <summary>
    /// ToMarkdownTable method with assigning align format of each column
    /// </summary>
    /// <param name="input">Input array</param>
    /// <param name="aligns">Align format of each column</param>
    /// <returns></returns>
    public static string[] ToMarkdownTable(string[,] input, Align[] aligns)
    {
        //    Null check
        if (ReferenceEquals(input, null))
        {
            throw new ArgumentNullException($"{nameof(input)} is null");
        }

        if (ReferenceEquals(input, null))
        {
            throw new ArgumentNullException($"{nameof(aligns)} is null");
        }

        long width = input.GetLongLength(1);
        long height = input.GetLongLength(0);

        string[] output = new string[height + 1];

        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.Append('|');
        for (int x = 0; x < width; x++)
        {
            stringBuilder.Append(input[0, x]);
            stringBuilder.Append('|');
        }
        output[0] = stringBuilder.ToString();
        stringBuilder.Clear();

        stringBuilder.Append('|');
        for (int x = 0; x < width; x++)
        {
            switch (aligns[x])
            {
                case Align.Right:
                    stringBuilder.Append("-:|");
                    break;
                case Align.Center:
                    stringBuilder.Append(":-:|");
                    break;
                case Align.Left:
                    stringBuilder.Append(":-|");
                    break;
                case Align.Default:
                    stringBuilder.Append("-|");
                    break;
                default:
                    stringBuilder.Append("-|");
                    break;
            }
            
        }
        output[1] = stringBuilder.ToString();
        stringBuilder.Clear();

        for (int y = 1; y < height; y++)
        {
            stringBuilder.Append('|');
            for (int x = 0; x < width; x++)
            {
                stringBuilder.Append(input[y, x]);
                stringBuilder.Append('|');
            }
            output[y + 1] = stringBuilder.ToString();
            stringBuilder.Clear();
        }
        return output;
    }
}

Тестовые кейсы

Тестовые примеры Converter.ToMarkdownTable методы включают string, sbyte, byte, short, ushort, char и int введите двумерные массивы ввода.

//    string type two dimensional array case
Console.WriteLine("string type two dimensional array case");
string[,] test_string = { { "0", "1", "1", "1" },
                          { "2", "3", "1", "1" },
                          { "0", "1", "1", "1" }};

var format = new Converter.Align[test_string.GetLongLength(1)];
for (int i = 0; i < test_string.GetLongLength(1); i++)
{
    format[i] = Converter.Align.Center;
}
var MarkdownTable = Converter.ToMarkdownTable(test_string, Converter.Align.Center);
for (int i = 0; i < MarkdownTable.Length; i++)
{
    Console.WriteLine(MarkdownTable[i]);
}
Console.WriteLine();

//    sbyte type two dimensional array case
Console.WriteLine("sbyte type two dimensional array case");
sbyte[,] test_sbyte = { { 0, 1, 1, 1 },
                        { 2, 3, 1, 1 },
                        { 0, 1, 1, 1 }};

var MarkdownTable_sbyte = Converter.ToMarkdownTable(test_sbyte, Converter.Align.Center);
for (int i = 0; i < MarkdownTable.Length; i++)
{
    Console.WriteLine(MarkdownTable[i]);
}
Console.WriteLine();

//    byte type two dimensional array case
Console.WriteLine("byte type two dimensional array case");
byte[,] test_byte = { { 0, 1, 1, 1 },
                      { 2, 3, 1, 1 },
                      { 0, 1, 1, 1 }};

var MarkdownTable_byte = Converter.ToMarkdownTable(test_byte, Converter.Align.Center);
for (int i = 0; i < MarkdownTable.Length; i++)
{
    Console.WriteLine(MarkdownTable[i]);
}
Console.WriteLine();

//    short type two dimensional array case
Console.WriteLine("short type two dimensional array case");
short[,] test_short = { { 0, 1, 1, 1 },
                        { 2, 3, 1, 1 },
                        { 0, 1, 1, 1 }};

var MarkdownTable_short = Converter.ToMarkdownTable(test_short, Converter.Align.Center);
for (int i = 0; i < MarkdownTable.Length; i++)
{
    Console.WriteLine(MarkdownTable[i]);
}
Console.WriteLine();

//    ushort type two dimensional array case
Console.WriteLine("ushort type two dimensional array case");
ushort[,] test_ushort = { { 0, 1, 1, 1 },
                          { 2, 3, 1, 1 },
                          { 0, 1, 1, 1 }};

var MarkdownTable_ushort = Converter.ToMarkdownTable(test_ushort, Converter.Align.Center);
for (int i = 0; i < MarkdownTable.Length; i++)
{
    Console.WriteLine(MarkdownTable[i]);
}
Console.WriteLine();

//    char type two dimensional array case
Console.WriteLine("char type two dimensional array case");
char[,] test_char = { { '0', '1', '1', '1' },
                      { '2', '3', '1', '1' },
                      { '0', '1', '1', '1' }};

var MarkdownTable_char = Converter.ToMarkdownTable(test_char, Converter.Align.Center);
for (int i = 0; i < MarkdownTable.Length; i++)
{
    Console.WriteLine(MarkdownTable[i]);
}
Console.WriteLine();

//    int type two dimensional array case
Console.WriteLine("int type two dimensional array case");
int[,] test_int = { { 0, 1, 1, 1 },
                    { 2, 3, 1, 1 },
                    { 0, 1, 1, 1 }};

var MarkdownTable_int = Converter.ToMarkdownTable(test_int, Converter.Align.Center);
for (int i = 0; i < MarkdownTable.Length; i++)
{
    Console.WriteLine(MarkdownTable[i]);
}
Console.WriteLine();

Результат вышеуказанных тестов:

string type two dimensional array case
|0|1|1|1|
|:-:|:-:|:-:|:-:|
|2|3|1|1|
|0|1|1|1|

sbyte type two dimensional array case
|0|1|1|1|
|:-:|:-:|:-:|:-:|
|2|3|1|1|
|0|1|1|1|

byte type two dimensional array case
|0|1|1|1|
|:-:|:-:|:-:|:-:|
|2|3|1|1|
|0|1|1|1|

short type two dimensional array case
|0|1|1|1|
|:-:|:-:|:-:|:-:|
|2|3|1|1|
|0|1|1|1|

ushort type two dimensional array case
|0|1|1|1|
|:-:|:-:|:-:|:-:|
|2|3|1|1|
|0|1|1|1|

char type two dimensional array case
|0|1|1|1|
|:-:|:-:|:-:|:-:|
|2|3|1|1|
|0|1|1|1|

int type two dimensional array case
|0|1|1|1|
|:-:|:-:|:-:|:-:|
|2|3|1|1|
|0|1|1|1|

Если есть какие-то улучшения, пожалуйста, дайте мне знать.

1 ответ
1

Вот мои наблюдения:

Align

  • Я думаю Alignment было бы лучше имя. (Выровнять это глагол, где Выравнивание это существительное)
  • В большинстве случаев Default, Unknown или же Invalid это первый элемент в перечислении
    • Если вы определяете переменную перечисления без указания ее значения, она будет объявлена ​​как 0
public enum Alignment { Default, Left, Center, Right }

ConvertAll

  • ReferenceEquals(array, null): Это можно упростить следующим образом: array is null
  • ArgumentNullException($"{nameof(array)} is null"): Здесь конструктор ожидает имя параметра, не сообщение. Итак, правильный способ выглядел бы так:
if (array is null) throw new ArgumentNullException(nameof(array));
  • array.GetLongLength(0): Как вы это делали в ToMarkdownTable вы можете объявить вспомогательную переменную для хранения этого значения и многократно использовать его:
var rowCount = array.GetLongLength(0);
var columnCount = array.GetLongLength(1);
var output = new TOutput[rowCount, columnCount];
  • Вложенные циклы for: это незначительно, но вы можете избавиться от операторов блока:
for (var row = 0; row < rowCount; row++)
    for (var column = 0; column < columnCount; column++)
        output[row, column] = converter(array[row, column]);

Так что весь ConvertAll можно реорганизовать следующим образом:

private static TOutput[,] ConvertAll<TInput, TOutput>(TInput[,] array, Converter<TInput, TOutput> converter)
{
    if (array is null) throw new ArgumentNullException(nameof(array));
    if (converter is null) throw new ArgumentNullException(nameof(converter));

    var rowCount = array.GetLongLength(0);
    var columnCount = array.GetLongLength(1);
    var output = new TOutput[rowCount, columnCount];

    for (var row = 0; row < rowCount; row++)
        for (var column = 0; column < columnCount; column++)
            output[row, column] = converter(array[row, column]);

    return output;
}

ToMarkDownTable где находится основная логика

  • И снова нулевые проверки можно упростить
    • Вторая нулевая проверка выполняется против input, пожалуйста, исправьте это, чтобы использовать alings там
  • width и height: Я бы посоветовал придерживаться условий row и column потому что мы говорим о таблице.
  • stringBuilder.Clear(): Несмотря на то, что это действительно работает, это указывает на то, что ваше разделение проблем нехорошо.
    • Я предлагаю ввести несколько вспомогательных методов и использовать их следующим образом:
public static string[] ToMarkDownTable(string[,] input, Alignment[] aligns)
{
    if (input is null) throw new ArgumentNullException(nameof(input));
    if (aligns is null) throw new ArgumentNullException(nameof(aligns));

    var rowCount = input.GetLongLength(0);
    var output = new string[rowCount + 1];

    output[0] = ToTableHeader(input);
    output[1] = ToTableCellAlignments(input, aligns);

    for (var row = 1; row < rowCount; row++)
        output[row + 1] = ToTableRow(input, row);

    return output;
}

Заголовок

  • stringBuilder.Append('|');: Я предлагаю ввести константу уровня класса для хранения разделителя ячеек:
private const char CellSeparator="|";
  • При этом генерация заголовка будет свободной от строкового литерала:
private static string ToTableHeader(string[,] input)
{
    var tableHeaderBuilder = new StringBuilder();
    tableHeaderBuilder.Append(CellSeparator);

    for (var column = 0; column < input.GetLongLength(1); column++)
    {
        tableHeaderBuilder.Append(input[0, column]);
        tableHeaderBuilder.Append(CellSeparator);
    }

    return tableHeaderBuilder.ToString();
}
  • Если твой input будет многомерным массивом, а не зубчатым, тогда input может быть одномерным массивом. Это увеличило бы разделение.

Выравнивания ячеек

  • stringBuilder.Append: Это повторялось слишком много раз.
    • Ваше ветвление должно быть ограничено данными (НЕ должно включать операцию).
    • При этом вы сможете использовать выражение переключателя:
private static string ToTableCellAlignments(string[,] input, Alignment[] aligns)
{
    var tableCellAlignmentBuilder = new StringBuilder();
    tableCellAlignmentBuilder.Append(CellSeparator);

    for (var column = 0; column < input.GetLongLength(1); column++)
    {
        var separator = aligns[column] switch
        {
            Alignment.Right => $"-:{CellSeparator}",
            Alignment.Center => $":-:{CellSeparator}",
            Alignment.Left => $":-{CellSeparator}",
            Alignment.Default => $"-{CellSeparator}",
            _ => $"-{CellSeparator}"
        };

        tableCellAlignmentBuilder.Append(separator);
    }

    return tableCellAlignmentBuilder.ToString();
}

Ряд

  • Я решил извлечь только одну генерацию строки вместо всех строк
    • Это упрощает логику генерации строк.
    • и может быть легко изменен для использования многомерного массива
private static string ToTableRow(string[,] input, int row)
{
    var tableRowBuilder = new StringBuilder();
    tableRowBuilder.Append(CellSeparator);

    for (var column = 0; column < input.GetLongLength(1); column++)
    {
        tableRowBuilder.Append(input[row, column]);
        tableRowBuilder.Append(CellSeparator);
    }
    return tableRowBuilder.ToString();
}

В случае многомерных массивов основная логика будет выглядеть так:

  • Итак, на мой взгляд, имеет смысл преобразовать ваш зубчатый массив в многомерный
private static string[] ToMarkDownTable(string[][] input, Alignment[] aligns)
{
    if (input is null) throw new ArgumentNullException(nameof(input));
    if (aligns is null) throw new ArgumentNullException(nameof(aligns));

    var rowCount = input.GetLongLength(0);
    var columnCount = input.GetLongLength(1);
    var output = new string[rowCount + 1];

    output[0] = ToTableHeader(input[0]);
    output[1] = ToTableCellAlignments(columnCount, aligns);

    for (var row = 1; row < rowCount; row++)
        output[row + 1] = ToTableRow(input[row]);

    return output;
}

private const char CellSeparator="|";
private static string ToTableHeader(string[] headers)
{
    var tableHeaderBuilder = new StringBuilder();
    tableHeaderBuilder.Append(CellSeparator);

    foreach (var header in headers)
    {
        tableHeaderBuilder.Append(header);
        tableHeaderBuilder.Append(CellSeparator);
    }

    return tableHeaderBuilder.ToString();
}

private static string ToTableCellAlignments(long columns, Alignment[] aligns)
{
    var tableCellAlignmentBuilder = new StringBuilder();
    tableCellAlignmentBuilder.Append(CellSeparator);

    for (var column = 0; column < columns; column++)
    {
        var separator = aligns[column] switch
        {
            Alignment.Right => $"-:{CellSeparator}",
            Alignment.Center => $":-:{CellSeparator}",
            Alignment.Left => $":-{CellSeparator}",
            _ => $"-{CellSeparator}"
        };

        tableCellAlignmentBuilder.Append(separator);
    }

    return tableCellAlignmentBuilder.ToString();
}

private static string ToTableRow(string[] cells)
{
    var tableRowBuilder = new StringBuilder();
    tableRowBuilder.Append(CellSeparator);

    foreach (var cell in cells)
    {
        tableRowBuilder.Append(cell);
        tableRowBuilder.Append(CellSeparator);
    }
    return tableRowBuilder.ToString();
}
```

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *