让它抓住多个,以及添加Where子句?

时间:2017-10-25 10:59:02

标签: c# .net

我正在尝试创建一个方法,它允许我从一个集合中获取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);
}

5 个答案:

答案 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):

  1. 如果source是ICollection / ICollection,您可以从中获取元素数量,生成随机数和pick和元素

  2. 如果它只是一个序列,如果你对它进行计数然后随机元素-e你会枚举它两次。相反,你逐个一个地(通过显式获取迭代器)遍历它,然后用迭代器中的元素更新随机元素的概念,概率为1/n,其中n是到目前为止访问过的元素的数量。最后,每个元素都有相同的选择变化。

  3. 从其他方法运行此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;
            }
        }
    }