Я пытаюсь реализовать конвертер, который может преобразовывать двумерный массив (например, 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 ответ
Вот мои наблюдения:
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();
}
```