从另一个集合中尚不存在的集合中获取随机项 - LINQ?

时间:2010-09-09 03:08:49

标签: linq

我正在努力学习LINQ,但一开始就很混乱!

我有一组具有颜色属性(MyColor)的项目。我有另一个所有颜色的集合(称为AvailableColors - 例如10个)。

我想从我的集合中尚不存在的AvailableColors中获取随机颜色。

我当前的C#代码只是一个随机颜色但是我想在LINQ中重写它以接收当前的颜色集合并从可能的选项中排除它们:

public MyColor GetRandomColour()
{
    return AvailableColors[new Random().Next(0, AvailableColors.Count)];
}

所以它将采用现有的集合:

public MyColor GetRandomColour(ListOfSomethingWithColorProperty)

感谢您的任何指示!

5 个答案:

答案 0 :(得分:2)

排除已使用的颜色意味着保存状态。您最好writing an iterator并使用yield return返回序列中的下一个随机颜色。这使您可以“记住”已使用的颜色。

如果您愿意,可以使用Linq的Take(1)进行调用。

答案 1 :(得分:0)

 // assumes Random object is available, preferrably a re-used instance
 Color color = AvailableColors
                 .Except(myItems.Select(item => item.Color).Distinct())
                 .OrderBy(c => random.Next())
                 .FirstOrDefault();

可能不是非常有效,但也可能不是一个问题,因为少数项目。

另一种方法是事先对可用颜色进行一次随机排序,因此您可以按顺序排列。使用List<Color>以便您可以在使用时删除元素,或者在每次拉动时保存当前索引。一旦列表耗尽或索引超过阵列的长度,请通知您的用户您已全部没有颜色。

答案 2 :(得分:0)

var rnd = new Random();   // don't keep recreating a Random object.


public MyColor GetRandomColour(List<Something> coll)  
{
   var len = rnd.Next(0, AvailableColors.Count- coll.Count);
   return AvailableColors.Except(coll.Select(s=>s.MyColor)).Skip(len).First();
}

答案 3 :(得分:0)

我建议您Linq-minded并创建一个好的,通用的IEnumerable<T>扩展方法来完成您需要的繁重工作,然后您的GetRandomColor函数更简单,您可以将扩展方法用于其他类似任务。

首先,定义这个扩展方法:

public static IEnumerable<T> SelectRandom<T>(this IEnumerable<T> @this, int take)
{
    if (@this == null)
    {
        return null;
    }
    var count = @this.Count();
    if (count == 0)
    {
        return Enumerable.Empty<T>();
    }
    var rnd = new Random();
    return from _ in Enumerable.Range(0, take)
           let index = rnd.Next(0, count)
           select @this.ElementAt(index);
}

此功能允许您从任何IEnumerable<T>中选择零个或多个随机选择的元素。

现在您的GetRandomColor功能如下:

public static MyColor GetRandomColour()
{
    return AvailableColors.SelectRandom(1).First();
}

public static MyColor GetRandomColour(IEnumerable<MyColor> except)
{
    return AvailableColors.Except(except).SelectRandom(1).First();
}

第二个函数接受IEnumerable<MyColor>以从可用颜色中排除,因此要调用此函数,您需要从项目集合中选择MyColor属性。由于您没有指定此集合的类型,因此我认为最好使用IEnumerable<MyColor>而不是组成类型或定义不必要的接口。

所以,调用代码现在看起来像这样:

var myRandomColor = GetRandomColour(collectionOfItems.Select(o => o.MyColor));

或者,您可以直接依赖Linq和新创建的扩展方法并执行此操作:

var myRandomColor =
    AvailableColors
    .Except(collectionOfItems.Select(o => o.MyColor))
    .SelectRandom(1)
    .First();

此替代方案更具可读性和可理解性,有助于代码的可维护性。请享用。

答案 4 :(得分:0)

从序列中选择随机元素有一种很好的方法。这里它被实现为一种扩展方法:

public static T Random<T>(this IEnumerable<T> enumerable)
{
    var rng = new Random(Guid.NewGuid().GetHashCode());
    int totalCount = 0;
    T selected = default(T);

    foreach (var data in enumerable)
    {
        int r = rng.Next(totalCount + 1);
        if (r >= totalCount)
            selected = data;
        totalCount++;
    }
    return selected;
}

该方法使用以下事实:当迭代为1 / n时,在第m个选择第n个元素的概率。

使用此方法,您可以在一行中选择颜色:

var color = AvailableColors.Except(UsedColors).Random();