【问题标题】:Implementing skip/take pagination via pageIndex/pageSize pagination when skip is not divisible by take当skip不能被take整除时,通过pageIndex/pageSize分页实现skip/take分页
【发布时间】:2023-03-24 13:20:01
【问题描述】:

假设我有一些神奇的分页黑盒类,它使用pageIndexpageSize 检索数据,如下所示:

public class PaginatedList<T>
{
    // ...

    // Chops up the internally stored list into pages of size "pageSize",
    // then returns the page stored at index "pageIndex".
    public IEnumerable<T> Get(int pageIndex, int pageSize)
    {
        // Magic black box code goes here.
    }

    // ...
}

现在假设我有一个驱动类想要使用这个PaginatedList 类,但是想要通过使用skiptake 参数来实现分页。当然,如果我想要 skip 的偏移量恰好可以被我想要 take 的数量整除,那么我可以通过执行以下操作来实现:

public class MyDriver
{
    // Bypass the first "skip" elements and return the next "take" elements.
    static IEnumerable<T> OffsetGet(PaginatedList<T> myList, int skip, int take)
    {
        // ASSERT: skip % take == 0 is true.
        return myList.Get(skip/take, take);
    }

    static void Main(string[] args)
    {
        // ...

        // From some dataSource, store some strings in a fancy PaginatedList.
        var myList = new PaginatedList<string>(dataSource);

        // Skip the first 20 strings and take the next 5 strings.
        var myData = OffsetGet(myList, 20, 5);

        // ...
    }
}

但是,如果我想要 skip 的偏移量是 not可以被我想要take的数量整除?

抱歉,如果这个问题已经在某个地方得到了回答,或者上面的代码太模糊了;这是我在 StackOverflow 上的第一个问题,所以请保持温和。 =]

【问题讨论】:

  • 好问题。我想知道这是否只是找到 GCD 的问题,但事情并没有那么简单……您对只获取最少的数据量有多少关心?
  • 另外,pageIndex 是从零开始还是从 1 开始?
  • @JonSkeet 谢谢。如果我们最终获取一些额外的记录然后修剪结果,我不会太在意;主要瓶颈是Get 调用,不应多次调用。
  • @astander 嗯,我不确定。我怀疑它必须是从零开始的,因为OffsetGet(myList, 20, 5) 应该等同于Get(4, 5)。不过,我可能会遇到一些错误。随意将索引更改为适合您的任何内容。

标签: c# pagination ienumerable


【解决方案1】:

这部分取决于您希望在代码复杂性与数据获取效率之间取得多少平衡。

你可以总是通过获取两倍于你需要的数据来成功:

int virtualTake = take * 2;
int virtualSkip = skip / virtualTake;
var bigList = Get(myList, virtualSkip / virtualTake, virtualTake);
int offset = virtualSkip % take;
var pruned = bigList.Skip(offset).Take(take);

但是,在许多情况下,这会获取比您需要的更多的数据。您至少可以针对skip 已经是take 的倍数的情况进行优化,但还有其他情况可以改进。

例如,如果你想跳过 23 并取 5,上述将有效跳过 20 并取 10,然后修剪...但你可以跳过 21并取 7 代替。下面根据 cmets 中 astander 的建议编写代码:

int newTake = take;
while ((skip / newTake + 1) * newTake < (skip + take))
{
     newTake++;
}
var bigList = Get(myList, skip / newTake, newTake);
int offset = skip % newTake;
var pruned = bigList.Skip(offset).Take(take);

【讨论】:

  • int newTake = take; while ((skip / newTake + 1) * newTake &lt; (skip + take)) newTake++; int newSkip = (skip / newTake) * newTake;这样的东西怎么样
  • 我对几个窗口进行了测试,它似乎可以按预期工作,返回与take 大小重叠的最小窗口大小
  • @astander:是的,这很可能。对于非常大的take 值,它可能比过度获取效率低——但这实际上取决于我们不知道的事情。介意我将该代码添加到我的答案中吗? (或者您当然可以创建自己的答案...)
  • 请继续并将其添加到答案中。
  • @astander:完成。我认为我已经适当地调整了它:)
【解决方案2】:

你可以直接使用 LINQ 的 Skip 和 Take 方法

return mylist.Skip(skip).Take(take).AsEnumerable();

【讨论】:

  • 我认为您错过了问题的重点。 OP 只能访问 Get 方法,并且想要确定要传递的参数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-26
  • 2017-09-20
  • 1970-01-01
  • 2014-01-12
  • 2012-06-12
  • 1970-01-01
相关资源
最近更新 更多