Отформатируйте строку как показано ниже - PullRequest
0 голосов
/ 24 октября 2019

У меня есть список книг и опубликованных дат, которые мне нужно будет отформатировать, как показано ниже

Book    Date    Book    Date
ABC.....12-18   1ABC....12-18     
ABCD....12-18   ABC123..12-18        
ABCDEF..12-18   ABz.....12-18       
X.......12-18   ABCzz...12-18       
AIJKL...12-18   ABCdfs..12-18 
ABC.....12-18   1ABC....12-18     
ABCD....12-18   ABC123..12-18        
ABCDEF..12-18   ABz.....12-18       
X.......12-18   ABCzz...12-18       
AIJKL...12-18   ABCdfs..12-18          

Я пробовал stringbuilder

List<Book> lstBooks = GetBooks();    
StringBuilder books = new StringBuilder();
books = books.AppendLine(" Book    Date    Book    Date ");

foreach (Book b in lstBooks )
{
    books.Append(b.Name + ".....".PadLeft(5 - b.CompletedDate.Length) + Environment.NewLine);
}

Но показ данных рядомгде я застрял, любая помощь будет оценена.

Ответы [ 5 ]

4 голосов
/ 24 октября 2019
  • Для отображения данных в столбцах одинакового размера требуются два цикла данных, первый цикл должен получить максимальную длину отображаемого текста, затем второй цикл использует PadRight, чтобы добавить достаточно .'символы для выравнивания текста по левому краю, как у вас в примере.
  • Используйте PadRight для выравнивания текста по левому краю и PadLeft для выравнивания текста по правому краю.
  • PadLeftи аргумент PadRight представляет собой максимальную длину строки, а не количество добавляемых символов заполнения.
  • Используйте явный формат даты MM-yy, чтобы обеспечить вывод всего 5 символов(и чтобы избежать двусмысленности, вы должны использовать форматы ISO 8601 и всегда избегать двухзначных лет).
  • Для отображения нескольких столбцов в одной строке требуется отдельный счетчик для подсчета количества элементов, записанных в текущей строке. и только добавление разрыва строки после записи 2 элементов в строку.

    List<Book> books = GetBooks();

    StringBuilder sb = new StringBuilder();

    const Int32 maxPerLine = 2;

    Int32 longestName = books.Max( b => b.Name.Length ) + 1; // Determine the width of the column by finding the longest text in the data. `Max` is a Linq extension method. Then add 1 to ensure there's always at least 1 dot between the name and the date.

    // Render column headings:
    for( Int32 i = 0; i < maxPerLine; i++ )
    {
        sb.Append( "Book".PadRight( longestName ) );
        sb.Append( "Date".PadRight( 5 ) );
        sb.Append( "  " );
    }
    sb.AppendLine();

    // Render the book names and dates:
    Int32 i = 0;
    foreach( Book book in books ) 
    {
        if( i > 0 && i % maxPerLine == 0 ) sb.AppendLine();

        String namePadded = book.Name.PadRight( longestName + 1, '.' ); // +1 
        sb.Append( namePadded );
        sb.Append( book.CompletedDate.ToString("MM-yy") );

        sb.Append( "  " );

        i++;
    }
3 голосов
/ 24 октября 2019

Итак, я дам вам вариант, который вам нужно будет расширить, чтобы работать для всех сценариев, но он должен помочь вам, если вам это нравится. Я также использую список строк в качестве простого примера;у вас не должно возникнуть проблем с его применением к Book s.

У вас есть один список элементов, который вам нужно отобразить в двух четных столбцах, так что вы можете разделить оригинал на два и Zip их резервное копирование;вот так:

var strings = new List<string>
{
    "first",
    "second",
    "third",
    "fourth"
};

var half = strings.Count / 2;
strings
    .Take(half)
    .Zip(strings.Skip(half), (f, s) => $"{f} : {s}")
    .ToList()
    .ForEach(Console.WriteLine);

Вывод:
первый: третий
второй: четвертый

Отслеживание: Что происходит при нечетном количестве элементов?

2 голосов
/ 25 октября 2019

Одним из способов вывода списка элементов в формате столбца является определение количества строк (путем деления количества элементов на количество столбцов и добавления 1, если количество не делится равномерно на количество столбцов), изатем выведите «количество столбцов» количество элементов, отформатированных по ширине столбца.

Мы можем написать метод, который принимает список строк, а затем выводит строки в столбцы определенной ширины:

public static void OutputInColumns(List<string> items, int columnCount,
    int columnWidth, string header = null)
{
    // If no columns or no items are specified, return
    if (columnCount == 0 || items?.Any() != true) return;
    var count = items.Count;

    // Determine how many rows we need and fill in last row with empty values if needed
    var rows = count / columnCount + (count % columnCount > 0 ? 1 : 0);
    items.AddRange(Enumerable.Range(0, 
        columnCount - count % columnCount).Select(x => string.Empty));

    // Output our column headers
    if (!string.IsNullOrEmpty(header))
        Console.WriteLine(string.Join(" ║ ",
            Enumerable.Range(0, columnCount)
                .Select(x => header.PadRight(columnWidth, ' '))));

    // Output a divider
    if (!string.IsNullOrEmpty(header))
        Console.WriteLine(string.Join("═╬═",
            Enumerable.Range(0, columnCount)
                .Select(x => new string('═', columnWidth))));

    // Output our row data
    for (int row = 0; row < rows; row++)
    {
        // For each row, add a line with items separated by a tab
        Console.WriteLine(string.Join(" ║ ", items
            .Skip(row * columnCount)
            .Take(columnCount)
            .Select(item => item
                .Substring(0, Math.Min(item.Length, columnWidth))
                .PadRight(columnWidth, ' '))));
    }
}

Далее мы можем написать метод, который принимает Book и выводит строку в формате "Title....PublishDate". Мы можем позволить пользователю передать значение ширины части Title и по умолчанию показывать весь заголовок, если ширина не указана:

public static string AsColumnString(Book book, int columnWidth = 0)
{
    if (columnWidth < 1) columnWidth = book.Title.Length + 8;
    var name = book.Title.Substring(0, Math.Min(book.Title.Length, columnWidth - 8))
        .PadRight(columnWidth - 5, '.');
    var date = book.PublishDate.ToString("MM-yy");
    return $"{name}{date}";
}

Теперь, если у нас есть списоккниг, мы можем легко напечатать их в любом количестве столбцов (с любой шириной столбца, которую мы выбираем):

public static void Main(string[] args)
{
    var books = new List<Book>
    {
        new Book {Title = "Ulysses", PublishDate = DateTime.Parse("February 2, 1922")},
        new Book {Title = "Don Quixote", PublishDate = DateTime.Parse("January 16, 1605")},
        new Book {Title = "The Great Gatsby", PublishDate = DateTime.Parse("April 10, 1925")},
        new Book {Title = "Moby Dick", PublishDate = DateTime.Parse("October 18, 1851")},
        new Book {Title = "War and Peace", PublishDate = DateTime.Parse("January 1, 1869")},
        new Book {Title = "Hamlet", PublishDate = DateTime.Parse("January 1, 1603")},
        new Book {Title = "To Kill a Mockingbird", PublishDate = DateTime.Parse("July 11, 1960")},
        new Book {Title = "The Catcher in the Rye", PublishDate = DateTime.Parse("July 16, 1951")},
        new Book {Title = "The Hobbit", PublishDate = DateTime.Parse("September 21, 1937")},
        new Book {Title = "Fahrenheit 451", PublishDate = DateTime.Parse("October 19, 1953")},
        new Book {Title = "The Handmaid's Tale", PublishDate = DateTime.Parse("January 1, 1985")},
    };

    // Select the longest book title and add '8' for the three dots and the date
    var columnWidth = books.Select(b => b.Title.Length).Max() + 8;
    var columnCount = 2;

    // Create our header for each column
    var header = "Book".PadRight(columnWidth - 5) + "Date";

    OutputInColumns(books.Select(b => AsColumnString(b, columnWidth)).ToList(), 
        columnCount, columnWidth, header);
}

Вывод

enter image description here

Вот еще один пример, только на этот раз с использованием 4 более узких столбцов:

// Note we can make our columns smaller and add more of them
columnWidth = 16;
columnCount = 4;
header = "Book".PadRight(columnWidth - 5) + "Date";
OutputInColumns(books.Select(b => AsColumnString(b, columnWidth)).ToList(), columnCount, 
    columnWidth, header);

Вывод

enter image description here

Примечание. Возможно, вы захотите указать полный год в качестве даты, поскольку используемые мной образцы охватывают несколько веков, последние две цифры не очень полезны. :)

Заголовок

1 голос
/ 24 октября 2019

Выполните следующие действия:

    static void Main(string[] args)
    {
        List<Book> lstBooks = GetBooks();
        StringBuilder books = new StringBuilder();
        books = books.AppendLine("Book    Date    Book    Date");

        string dots = "........";

        foreach (Book b in lstBooks)
        {
            string neededDots = dots;

            for (int i = 1; i <= b.Name.Length; i++)
            {
                if (i > 0 && neededDots.Length > 1)
                {
                    neededDots = neededDots.Remove(0, 1);
                }
            }

            books.AppendLine(b.Name + neededDots.PadLeft(5 - b.CompletedDate.Length) + b.CompletedDate
                + "   "
                + b.Name + neededDots.PadLeft(5 - b.CompletedDate.Length) + b.CompletedDate);
        }

        Console.WriteLine(books);
        Console.Read();
    }

Вывод:

Book    Date    Book    Date
ABC.....12-18   ABC.....12-18
ABCD....12-18   ABCD....12-18
ABCDEF..12-18   ABCDEF..12-18
X.......12-18   X.......12-18
AIJKL...12-18   AIJKL...12-18
ABC.....12-18   ABC.....12-18
ABCD....12-18   ABCD....12-18
ABCDEF..12-18   ABCDEF..12-18
X.......12-18   X.......12-18
AIJKL...12-18   AIJKL...12-18
0 голосов
/ 25 октября 2019

Принимая во внимание другие ответы, я предоставлю цикл For-Loop с использованием индексного подхода.


Определение типов с помощью фиктивных данных

public class Book
{
    public string Name { get; set; }
    public DateTime CompletedDate { get; set; }
}

List<Book> lstBooks = new List<Book>
{
    new Book { Name = "ABC", CompletedDate = DateTime.Now },
    new Book { Name = "1ABC", CompletedDate = DateTime.Now },
    new Book { Name = "XYZ", CompletedDate = DateTime.Now },
    new Book { Name = "2XYZ", CompletedDate = DateTime.Now },
    new Book { Name = "123ABC", CompletedDate = DateTime.Now },
};

Пример кода

StringBuilder sBuilder = new StringBuilder();
string completedDateFormat = "MM-dd";

var spacePadding = 4;
var bookDatePadding = Math.Max(5, lstBooks.Max(b => b.Name.Length)) + 1;
var dateSpacePadding = completedDateFormat.Length;

string title = string.Join(string.Empty.PadRight(spacePadding, ' '),
    string.Join(string.Empty, "Book".PadRight(bookDatePadding, ' '), "Date".PadRight(dateSpacePadding, ' ')),
    string.Join(string.Empty, "Book".PadRight(bookDatePadding, ' '), "Date".PadRight(dateSpacePadding, ' ')));

sBuilder.AppendLine(title);

for (int i = 1, max = lstBooks.Count; i < max; i += 2)
{
    sBuilder.AppendLine(string.Join(string.Empty.PadRight(spacePadding, ' '),
        string.Join(string.Empty, lstBooks[i - 1].Name.PadRight(bookDatePadding, '.'), lstBooks[i - 1].CompletedDate.ToString(completedDateFormat)),
        string.Join(string.Empty, lstBooks[i].Name.PadRight(bookDatePadding, '.'), lstBooks[i - 1].CompletedDate.ToString(completedDateFormat))));
}

// Determine if Last record is accounted for by loop logic by looking at the remainder of a modular operation
var appendLastRecord = lstBooks.Count % 2 != 0;
if (appendLastRecord)
    sBuilder.AppendLine(string.Join(string.Empty, lstBooks.Last().Name.PadRight(bookDatePadding, '.'), lstBooks.Last().CompletedDate.ToString(completedDateFormat)));

Console.Write(sBuilder.ToString());

Это выведет следующее

Book   Date     Book   Date 
ABC....10-24    1ABC...10-24
XYZ....10-24    2XYZ...10-24
123ABC.10-24

Цикл For определяет i как итератор индекса;

  • это начальное значение будет 1, и мы будем добавлять 2 для каждой итерации lopp до тех пор, пока i не станет больше max, а точнее - длины списка,(i - 1 будет представлять более раннюю запись, i будет позже одной (или даже записи, но в массиве с 0 индексами, обратите внимание)

Из-за этого подхода мы имеемсценарий, в котором последняя запись не может быть представлена ​​в цикле - это нормально, поскольку тело этого цикла предназначено для обработки только 2 записей одновременно, и нам нужно обработать одну запись в этом событии.

РЕДАКТИРОВАТЬ 1:

Включая логику заполнения (хотя желание этого ответа состоит в том, чтобы представить подход индексации цикла, а не обязательно предоставлять законченный \ рабочий образец)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...