2010-09-09 36 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

相關問題