LINQおよびC#を使用してサブコレクションを選択するためのアルゴリズム/パターン
-
04-07-2019 - |
質問
文字列のC#コレクションがあります。各文字列は、ページに表示できる文です。 intのコレクションである改ページのコレクションもあります。文字列のコレクションが新しいページに分割されるインデックスを表します。
例:文字列コレクションの各10アイテムはページであるため、改ページのコレクションは、10、20、30の値を持つintのコレクションになります。...
したがって、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
}
この関数を書く際にいくつかの刺し傷を取り、範囲外で要求されている単一および2ページのドキュメントとページ番号を考慮する複雑なifおよびswitchステートメントを考え出し続けます(たとえば、ページ番号がページ数よりも大きく、ページ番号が0以下の場合は最初のページ)。
この問題との私の苦労は、このタイプのサブセットクエリに対処するためのよく知られたパターンまたはアルゴリズムはありますか?
解決
&quot;純粋な&quot; 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プロパティをlinqの.Count()メソッドに置き換えたくありません。 .GetRange()メソッドをlinqの.Skip(n).Take(m)メソッドに置き換えたくない場合。
これらのコレクションを他のコレクションに投影する場合は、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 );