Алгоритм/шаблон выбора подколлекций с использованием LINQ и C#
-
04-07-2019 - |
Вопрос
У меня есть коллекция строк C#.Каждая строка представляет собой предложение, которое может появиться на странице.У меня также есть коллекция разрывов страниц, которая представляет собой коллекцию целых чисел.представляющий индекс, в котором коллекция строк разбивается на новую страницу.
Пример:Каждые 10 элементов в коллекции строк представляют собой страницу, поэтому коллекция разрывов страниц будет коллекцией целых чисел со значениями 10, 20, 30....
Таким образом, если есть 2 страницы строк, то в коллекции разрывов страниц будет 1 элемент, а если есть 1 страница, то в коллекции разрывов страниц будет ноль элементов.
Я пытаюсь создать следующую функцию:
List<string> GetPage(List<string> docList, List<int> pageBreakList, int pageNum)
{
// This function returns a subset of docList - just the page requested
}
Я предпринял несколько попыток написать эту функцию и продолжаю придумывать сложные операторы if и switch, чтобы учитывать одно- и двухстраничные документы и номера страниц, запрашиваемые за пределами диапазона (например,последняя страница должна быть возвращена, если номер страницы больше количества страниц, и первая страница, если номер страницы равен 0 или меньше).
Моя борьба с этой проблемой заставляет меня задать вопрос:Существует ли хорошо известный шаблон или алгоритм для решения этого типа подмножества запросов?
Решение
«Чистый» Linq не подходит для решения этой проблемы.Лучше всего полагаться на методы и свойства List(T).Особых случаев не так уж и много.
//pageNum is zero-based.
List<string> GetPage(List<string> docList, List<int> pageBreaks, int pageNum)
{
// 0 page case
if (pageBreaks.Count != 0)
{
return docList;
}
int lastPage = pageBreaks.Count;
//requestedPage is after the lastPage case
if (requestedPage > lastPage)
{
requestedPage = lastPage;
}
int firstLine = requestedPage == 0 ? 0 :
pageBreaks[requestedPage-1];
int lastLine = requestedPage == lastPage ? docList.Count :
pageBreaks[requestedPage];
//lastLine is excluded. 6 - 3 = 3 - 3, 4, 5
int howManyLines = lastLine - firstLine;
return docList.GetRange(firstLine, howManyLines);
}
Вы не хотите заменять свойство .Count методом .Count() linq.Вы не хотите заменять метод .GetRange() методами .Skip(n).Take(m) linq.
Linq подойдет лучше, если вы захотите спроецировать эти коллекции в другие коллекции:
IEnumerable<Page> pages =
Enumerable.Repeat(0, 1)
.Concat(pageBreaks)
.Select
(
(p, i) => new Page()
{
PageNumber = i,
Lines =
docList.GetRange(p, ((i != pageBreaks.Count) ? pageBreaks[i] : docList.Count) - p)
}
);
Другие советы
Не уверен, для чего предназначен список разрывов страниц. Я бы подумал об этом так. Коллекция строк, номер страницы и размер страницы. Тогда вы могли бы сделать что-то вроде:
List<string> strings = ...
int pageNum = ...
int pageSze = ...
if (pageNum < 1) pageNum = 1;
if (pageSize < 1) pageSize = 1;
List<string> pageOfStrings = strings.Skip( pageSize*(pageNum-1) ).Take( pageSize ).ToList();
В случае, если количество страниц на странице зависит от вашего комментария, попробуйте что-то вроде ниже. Возможно, вам придется настроить проверку состояния кромки ...
List<string> strings = ...
List<int> sizes = ...
int pageNum = ...
int itemsToSkip = 0;
int itemsToTake = 1;
if (pageNum > 1)
{
sizes.Take( pageNum - 2).Sum();
if (pageNum <= sizes.Count)
{
itemsToTake = sizes[pageNum-1]
}
{
List<string> pageOfStrings = strings.Skip( itemsToSkip ).Take( itemsToTake );