【问题标题】:Linq order by random without GUIDLinq 随机排序,没有 GUID
【发布时间】:2017-04-14 15:08:57
【问题描述】:

需要使用 linq 随机排序,我不能使用 GUID.NewGuid(),我需要这样的东西:.OrderBy(x => "somestring")。

假设一个移动应用程序会生成一个随机字符串并调用我的 WebApi,因为我维护分页移动应用程序将发送相同的随机字符串但不同的页码,因此每次发送随机字符串的顺序应该相同但不同跳过 ... 。

这怎么可能?如果不是字符串,可能是数字或为每个 linq 查询固定的任何内容。

编辑:

这是 WebApi

[Route("GetChannels/{id}/{word}/{page}/{randomstring}")]
public IEnumerable<Channels> GetChannels(int id, string word, int page, string randomstring)
    {

   ...

    if (canPage)
       {                
            var channels = db.Channels.Where(x => (id == 0) || (x.CategoryId == id))
            .Where(q => word == "0" ||
            (q.Title.Contains(word) || q.Desc.Contains(word)))
            .OrderBy(x => randomstring).Skip(skip).Take(pageSize).ToList();
        }

...

【问题讨论】:

  • 您能在问题描述中添加一些标点符号吗?正如它目前所写的那样,目前尚不清楚您现在拥有什么、如何调用 api 以及您期望得到什么
  • 我能问一下你为什么想要 GUID 吗?我认为 Jon Skeet 的答案使用 GUID
  • @SergeyBerezovskiy,我添加了更多细节
  • @farhang67 .OrderBy(x =&gt; randomstring) 将返回无序序列,因为 randomstring 对于所有项目都是相同的。你能描述一下你想要达到的目标吗?
  • @SergeyBerezovskiy 我想在每次打开应用程序时以随机顺序向移动应用程序用户显示结果,这样我就可以生成一些用于订购的字符串并将其发送到服务器,当他们分页时,应用程序将发送相同的字符串,因此排序将相同但页码不同...

标签: c# linq random


【解决方案1】:

如果您想以随机顺序返回结果,但如果您从客户端发送相同的字符串,则该顺序将相同,那么您可以使用Random 类来获取伪随机顺序。这个随机生成器可以接受 seed - 一个值,用于计算伪随机序列的起始值。因此,您正在传递string,您可以使用它的哈希码来获取种子的整数值:

        var seed = randomstring.GetHashCode();
        var random = new Random(seed);

        var channels = db.Channels
           .Where(x => (id == 0) || (x.CategoryId == id))
           .Where(q => word == "0" || (q.Title.Contains(word) || q.Desc.Contains(word)))
           .AsEnumerable() // randomizing should happen on client side
           .OrderBy(x => random.Next())
           .Skip(skip)
           .Take(pageSize)
           .ToList();    

【讨论】:

  • 一个不错的解决方案,但需要注意的是,如果列表在第一次调用和任何后续寻呼调用之间发生了变化,那么列表将完全改变。因此,如果您正在查看经常更改的内容,这将不会很好。实际上,由于大多数数据库在没有指定orderby 的情况下不保证顺序,因此即使没有更改任何记录也很可能会中断......
  • 您可以通过将.OrderBy(x=&gt;x.id) 放在.AsEnumerable() 之前来解决后面的问题。
  • @RobertMcKee,不幸的是,这不起作用。您无法处理添加项目和保存原始项目的原始顺序。例如。你有物品1,2,3。订购为2,1,3。又增加了一项。您想保留原始订单并返回2,1,3,4。下一个项目来了。您想保留原始订单并获得2,1,3,4,5。 IE。所有未来的项目都不会被洗牌。它们将按添加顺序返回。
  • 排序,但如果一个项目以不同的顺序返回或从结果中间插入/删除一个项目,您可以保持项目的相对顺序。如果您的原始查询返回了1,2,3 并且2 被删除,那么3 将被分配2 曾经使用的随机数,这将使它与原来的位置完全不同。
  • 一个小问题是 GetHashCode 保证在框架的不同实现上返回相同的值 - 32 vs 64 或核心等。
【解决方案2】:

采用@Sergey 已经非常出色的答案并解决一些小问题:

    var channels = db.Channels.AsQueryable();

    #region filtering
      if (id!=0)
        channels = channels.Where(x=>x.CategoryId == id);
      if (word!="0")
        channels = channels.Where(x=>x.Title.Contains(word) || x.Desc.Contains(word));
    #endregion filtering

    #region server-side ordering
      channels = channels.OrderBy(x=>x.id);
    #endregion server-side ordering

    #region client-side ordering
      var seed = randomstring.GetHashCode();
      var random = new Random(seed);
      var channelList = channels.ToList(); // force client side
      var maxid = channelList.Select(x=>id).Max();
      var keys = Enumerable
        .Range(0,maxid)
        .ToDictionary(x=>x,x=>random.Next());

      var sorted = channelList
        .OrderBy(x=>keys[x.id]);
    #endregion client-side ordering

    #region paging
      var results = sorted
        .Skip(skip)
        .Take(pageSize)
        .ToList();
    #endregion paging

在这种情况下,如果记录被添加(新的更高的 id)或删除(id 被删除),那么将保持记录的顺序,就好像该记录一直存在一样,因此结果可能会发生变化,但不会完全重新排序.如果频道的 id 很大,性能也可能很差。

实际上,对于大多数情况,我只是建议使用随机数,生成未分页的结果,然后使用该键将结果填充到缓存中以进行后续分页操作(如果无法将整个未分页的结果发送给客户端) )。

【讨论】:

    【解决方案3】:

    试试这样的

    Random rand = new Random();
                List<string> input = new List<string>();
                var results = input.Select(x => new { x = x, r = rand.Next() }).OrderBy(x => x.r).Select(x => x.x);
    

    【讨论】:

      猜你喜欢
      • 2012-03-16
      • 1970-01-01
      • 2012-12-04
      • 2012-11-19
      • 2012-01-05
      • 1970-01-01
      • 2010-10-13
      • 1970-01-01
      相关资源
      最近更新 更多