我如何在LINQ中使用First()但随机?

Ask*_*man 5 c# linq

在这样的列表中:

var colors = new List<string>{"green", "red", "blue", "black","purple"};
Run Code Online (Sandbox Code Playgroud)

我可以得到这样的第一个值:

var color = colors.First(c => c.StartsWidth("b")); //This will return the string with "blue"
Run Code Online (Sandbox Code Playgroud)

如果我想要一个符合条件的随机值,我怎么做呢?例如这样的事情:

Debug.log(colors.RandomFirst(c => c.StartsWidth("b"))) // Prints out black
Debug.log(colors.RandomFirst(c => c.StartsWidth("b"))) // Prints out black
Debug.log(colors.RandomFirst(c => c.StartsWidth("b"))) // Prints out blue
Debug.log(colors.RandomFirst(c => c.StartsWidth("b"))) // Prints out black
Run Code Online (Sandbox Code Playgroud)

如果匹配条件的列表中有多个条目,我想随机拉出其中一个条目.它(我需要它)是一个内联解决方案.谢谢.

Zei*_*kki 15

随机排序然后:

var rnd = new Random();
var color = colors.Where(c => c.StartsWith("b"))
                  .OrderBy(x => rnd.Next())
                  .First();
Run Code Online (Sandbox Code Playgroud)

以上为每个元素生成一个随机数,并按该数字对结果进行排序.

如果您只有2个符合条件的元素,您可能不会注意到随机结果.但您可以尝试下面的示例(使用下面的扩展方法):

var colors = Enumerable.Range(0, 100).Select(i => "b" + i);

var rnd = new Random();

for (int i = 0; i < 5; i++)
{
    Console.WriteLine(colors.RandomFirst(x => x.StartsWith("b"), rnd));
}
Run Code Online (Sandbox Code Playgroud)

输出:

b23
b73
b27
b11
b8
Run Code Online (Sandbox Code Playgroud)

您可以使用以下命令创建扩展方法RandomFirst:

public static class MyExtensions
{
    public static T RandomFirst<T>(this IEnumerable<T> source, Func<T, bool> predicate, 
                                                                                Random rnd)
    {
        return source.Where(predicate).OrderBy(i => rnd.Next()).First();
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

var rnd = new Random();
var color1 = colors.RandomFirst(x => x.StartsWith("b"), rnd);
var color2 = colors.RandomFirst(x => x.StartsWith("b"), rnd);
var color3 = colors.RandomFirst(x => x.StartsWith("b"), rnd);
Run Code Online (Sandbox Code Playgroud)

优化:

如果您担心性能,可以尝试这种优化方法(将大型列表的时间减少一半):

public static T RandomFirstOptimized<T>(this IEnumerable<T> source, 
                                        Func<T, bool> predicate, Random rnd)
{
    var matching = source.Where(predicate);

    int matchCount = matching.Count();
    if (matchCount == 0)
        matching.First(); // force the exception;

    return matching.ElementAt(rnd.Next(0, matchCount));
}
Run Code Online (Sandbox Code Playgroud)

  • @Steve因为它为每个元素生成一个随机数,并按该数字排序. (2认同)