Algoritmo / padrão para a selecção de sub-coleções usando LINQ e C #
-
04-07-2019 - |
Pergunta
Eu tenho um C # coleção de strings. Cada corda é uma frase que pode aparecer em uma página. Eu também tenho uma coleção de quebras de página que é uma coleção de int de. representando o índice onde a coleção de cordas são de divisão para uma nova página.
Exemplo: Cada 10 itens na coleção string é uma página para a coleta de quebras de página seria uma coleção de int do com os valores de 10, 20, 30. ...
Assim, se houver 2 páginas de cordas, em seguida, haverá um item na coleção de quebra de página e, se houver uma página, então a coleção de quebra de página iria ter zero itens.
Eu estou tentando criar a seguinte função:
List<string> GetPage(List<string> docList, List<int> pageBreakList, int pageNum)
{
// This function returns a subset of docList - just the page requested
}
Eu tenho tido algumas facadas em escrever esta função e continuar chegando com complexo if e switch de levar em conta única e dois documentos de página e números de página que está sendo solicitado fora do intervalo (por exemplo, a última página deve ser devolvido se número da página é maior do que o número de páginas e primeira página se o número de página é 0 ou menos).
A minha luta com este problema leva-me a fazer a pergunta: Existe um padrão bem conhecido ou algoritmo para resolver este tipo de subconjunto consulta ??p>?
Solução
"Pure" Linq não é um bom ajuste para este problema. O melhor ajuste é contar com os métodos e propriedades de List (T). Não há -that- muitos casos especiais.
//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);
}
Você não quer substituir a propriedade .Count com o método de linq .Count (). Você não quer para substituir o método com .Skip (n) .Pegue (m) métodos LINQ .GetRange ().
Linq seria um ajuste melhor se você queria projetar essas coleções em outras coleções:
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)
}
);
Outras dicas
Não é certo que a lista de quebras de página é para. Gostaria de pensar nisso desta maneira. Uma coleção de cordas, um número de página, eo tamanho da página. Então você poderia fazer algo como:
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();
No caso em que o número de páginas pode variar por página de acordo com o seu comentário, tente algo como abaixo. Pode ser necessário ajustar a verificação de condição de borda ...
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 );