2010-09-09 68 views
0

我想学习LINQ,但起初相当混乱!从另一个集合中尚不存在的集合中获取一个随机项目 - LINQ?

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

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

我目前的C#代码只是得到一个随机的颜色,但我想改写这个在LINQ取当前颜色收集并排除那些可能的选项:

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

所以它会采取在现有收藏:

public MyColor GetRandomColour(ListOfSomethingWithColorProperty) 

感谢您的指点!

回答

2

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

一旦你有了,你可以使用Linq的Take(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>,以便您可以在使用元素时删除元素,或者在每次拖动时保存当前索引。一旦列表耗尽或索引超出了阵列的长度,请通知用户您的颜色已全部用完。

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(); 
} 
+0

你要确实有,如果你把它作为一个领域要明确你的类型的'Random'声明。 (这让我咧嘴笑,因为我不特别喜欢'var'。) – 2010-09-09 03:25:14

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(); 

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

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; 
} 

此方法使用的事实,概率超过第m个选择第n个元素时迭代为1/N。

使用这种方法,你可以在一行中选择颜色:

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

你的缩进很混乱。是否在其上面的if()的'totalCount ++'部分? – 2010-09-09 18:15:38

+0

哎呀,对不起,我的坏。当然不是。 – Nevermind 2010-09-10 05:48:08

相关问题