代码之家  ›  专栏  ›  技术社区  ›  Luke101

如何使用LINQ获取随机对象

  •  26
  • Luke101  · 技术社区  · 15 年前

    我试图在Linq中得到一个随机对象。我就是这样做的。

    //get all the answers
    var Answers = q.Skip(1).Take(int.MaxValue);
    //get the random number by the number of answers
    int intRandomAnswer = r.Next(1, Answers.Count());
    int count = 0;
    
    //locate the answer
    foreach(var Answer in Answers)
    {
        if (count == intRandomAnswer)
        {
            SelectedPost = Answer;
            break;
        }
        count++;
    }
    

    这是最好的方法吗?

    9 回复  |  直到 8 年前
        1
  •  36
  •   codekaizen    10 年前

    如何:

    SelectedPost = q.ElementAt(r.Next(1, Answers.Count()));
    

    进一步阅读:

    下面的评论对密切相关的问题做出了很好的贡献,我将把它们包括在这里,因为正如@rouby指出的那样,寻找这些问题答案的人可能会找到这个答案,在这些情况下,这个答案是不正确的。

    整个输入的随机元素

    要使随机选择中的所有元素都成为候选元素,需要将输入更改为 r.Next :

    SelectedPost = Answers.ElementAt(r.Next(0, Answers.Count()));
    

    @zidad添加了一个有用的扩展方法,以获取序列中所有元素的随机元素:

    public static T Random<T>(this IEnumerable<T> enumerable)
    {
        if (enumerable == null)
        {
             throw new ArgumentNullException(nameof(enumerable));
        }
    
        // note: creating a Random instance each call may not be correct for you,
        // consider a thread-safe static instance
        var r = new Random();  
        var list = enumerable as IList<T> ?? enumerable.ToList(); 
        return list.Count == 0 ? default(T) : list[r.Next(0, list.Count)];
    }
    
        2
  •  9
  •   Community CDub    8 年前

    使用A Fisher-Yates-Durstenfeld shuffle .

    (您可以使用helper/extension方法 shuffle your IEnumerable<T> sequence . 或者,如果使用 IList<T> 你可以 perform an in-place shuffle ,如果您愿意的话。)

        3
  •  8
  •   BFree    15 年前

    另一种古怪的方法(对于较大的数据集来说不是最有效的方法):

    SelectedPost = q.OrderBy(qu => Guid.NewGuid()).First();
    
        4
  •  4
  •   Wiebe Tijsma    11 年前

    基于接受答案的一般扩展方法(不总是跳过第一个,只枚举可枚举一次):

     public static class EnumerableExtensions
        {
            public static T Random<T>(this IEnumerable<T> enumerable)
            {
                var r = new Random();
                var list = enumerable as IList<T> ?? enumerable.ToList();
                return list.ElementAt(r.Next(0, list.Count()));
            }
        }
    
        5
  •  3
  •   Garandy    11 年前
    var rand = new Random();
    var SelectedPost = q.Skip(rand.Next(0,q.Count)).Take(1);
    

    最理想的情况是,您只希望对单个值进行函数查询,所以您设置了skip/take以跳到与正在生成的随机数匹配的序列号(以数据集的项计数为界,因此基于max(pkey)的缺少行问题边界不是问题),然后在序列中的该点挂起第一个项。

    在SQL中,这与查询 SELECT Count(*) FROM q 然后 SELECT * FROM q LIMIT {0}, 1 在哪里? {0} rand.Next(0, count) 这应该是相当有效的。

        6
  •  2
  •   Dan    8 年前

    参加派对迟到了,但这是谷歌的一个高调结果。简明扼要的版本可以是:

    var rnd = new Random();
    var SelectedPost = q.OrderBy(x => rnd.Next()).Take(1);
    

    它的缺点是,它会对所有元素应用一个随机数,但是它很紧凑,并且可以很容易地修改为接受多个随机元素。

        7
  •  1
  •   Tod    11 年前

    我之所以发布答案是因为我没有足够的声誉发表评论。

    我喜欢这个答案:

    SelectedPost = q.ElementAt(r.Next(1, Answers.Count()));
    

    但是 ElementAt 是从零开始的,肯定是从1开始并返回answers.count(),最终可能会抛出一个超出范围的实体,并且永远不会得到第一个实体。

    不会

    SelectedPost = q.ElementAt(r.Next(0, Answers.Count() - 1));
    

    更好吗?

        8
  •  0
  •   Turnkey    15 年前

    提取所有答案并循环它们不是最有效的方法,因为您要从数据库中移动大量数据。如果您使用的是一个自动递增的整数主键,那么您应该得到主键的最大值,然后在该范围内找到随机整数。然后根据随机函数导出的主键直接得到单个答案。

        9
  •  0
  •   RASKOLNIKOV    11 年前

    我在数据库中有产品表,每次用户输入一个产品详细信息,我都想在下面的页面中显示10个类似的产品。在每次刷新中,这个列表必须是更改的。它必须是随机的。

    Linq看起来像这样

    var products =
                DataContextFactory.GetDataContext()
                    .Set<Product>()
                    .Where(x =>x.Id!=id)
                    .OrderBy(emp => Guid.NewGuid())
                    .Take(10).ToList();
    
    x.Id!=id 
    

    这只适用于未将所选产品放入列表。

    它工作得很好