我正在尝试创建一个方法,它允许我从一个集合中获取X个随机元素,我已编码一个来获得一个随机元素,但我需要制作"金额"参数问题,如何将其考虑在内?
我还需要添加一个Where,因为我只需要抓取集合中的类实例(HashSet)有一个名为' Enabled'的公共字段的元素。并且等于真。
private static readonly Random Random = new Random();
private static readonly object Sync = new object();
public static T RandomElement<T>(this IEnumerable<T> enumerable, int amount)
{
if (enumerable == null)
throw new ArgumentNullException(nameof(enumerable));
var count = enumerable.Count();
int ndx;
lock (Sync)
ndx = Random.Next(count); // returns non-negative number less than max
return enumerable.ElementAt(ndx);
}
答案 0 :(得分:1)
看看:https://stackoverflow.com/a/1653204/7866667(懒惰的评估,有效的shuffle扩展方法实现)
关于你问题的第二部分,你不需要添加Where
,你已经拥有它 - 将它全部整合到一个方法中就不会是惯用的LINQ你可能会还做!
鉴于上述情况,您正在尝试做的事情的呼吁将是:
var chosenStuff = enumerable
.Where(someCondition)
.Shuffle()
.Take(amount);
答案 1 :(得分:0)
我有一种模糊的感觉,MoreLinq有点点零碎可以做你想做的事情,也可以有你可以拼凑起来的东西来制作你想要的东西。
这是一个伟大的项目。看看吧!
答案 2 :(得分:0)
您可以执行以下操作
private static readonly Random Random = new Random();
private static readonly object Sync = new object();
public static IEnumerable<T> RandomElement<T>(this IEnumerable<T> enumerable, int amount)
{
if (enumerable == null)
throw new ArgumentNullException(nameof(enumerable));
var count = enumerable.Count();
int ndx;
lock (Sync)
ndx = Random.Next(count); // returns non-negative number less than max
return enumerable.Skip(ndx - amount).Take(amount);
}
注意,这将以原始顺序返回一个集合,但是处于随机位置
答案 3 :(得分:0)
由于“随机性”而忽略了多次获得同一个对象的机会&#39;你可以沿着这些方向做点什么:
private static readonly Random Random = new Random();
private static readonly object Sync = new object();
public static IEnumerable<T> RandomElements<T>(this IEnumerable<T> enumerable, int amount, Func<T, bool> filter)
{
if (enumerable == null)
{
throw new ArgumentNullException(nameof(enumerable));
}
var filtered = enumerable.Where(filter);
var take = Math.Min(filtered.Count(), amount);
for (var i = 0; i < take; i++)
{
lock (Sync)
{
var randomIndex = Random.Next(filtered.Count);
yield return filtered.ElementAt(randomIndex);
}
}
}
调用:
var randomElements = collection.RandomElements(10, (MyObject obj) => obj.Enabled);
如另一个答案所述;传递where子句可以通过在调用中包含linq where子句来替换,如下所示:
var randomElements = collection.Where(x => x.Enabled).RandomElements(10);
答案 4 :(得分:0)
尝试这种从序列生成随机元素的方法(RandomElement方法来自C#in Depth):
如果source是ICollection / ICollection,您可以从中获取元素数量,生成随机数和pick和元素
如果它只是一个序列,如果你对它进行计数然后随机元素-e你会枚举它两次。相反,你逐个一个地(通过显式获取迭代器)遍历它,然后用迭代器中的元素更新随机元素的概念,概率为1/n
,其中n
是到目前为止访问过的元素的数量。最后,每个元素都有相同的选择变化。
从其他方法运行此n
次。
static class RandomExtensions
{
public static IList<T> PickNRandomElements<T>(this IEnumerable<T> source, Random random, int count)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (random == null)
{
throw new ArgumentNullException(nameof(random));
}
var result = new List<T>();
for (var i = 0; i < count; i++)
{
result.Add(source.RandomElement(random));
}
return result;
}
private static T RandomElement<T>(this IEnumerable<T> source, Random random)
{
ICollection collection = source as ICollection;
if (collection != null)
{
int count = collection.Count;
if (count == 0)
{
throw new InvalidOperationException("Sequence was empty.");
}
int index = random.Next(count);
return source.ElementAt(index);
}
ICollection<T> genericCollection = source as ICollection<T>;
if (genericCollection != null)
{
int count = genericCollection.Count;
if (count == 0)
{
throw new InvalidOperationException("Sequence was empty.");
}
int index = random.Next(count);
return source.ElementAt(index);
}
using (IEnumerator<T> iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
throw new InvalidOperationException("Sequence was empty.");
}
int countSoFar = 1;
T current = iterator.Current;
while (iterator.MoveNext())
{
countSoFar++;
if (random.Next(countSoFar) == 0)
{
current = iterator.Current;
}
}
return current;
}
}
}