2009-12-08 50 views
3

我有一個方法從數據庫中返回一個通用列表集合(List)。此集合已獲得訂單詳細信息,即訂單ID,訂單名稱,產品詳細信息等。從集合中隨機返回項目

此外,該方法返回的集合僅包含按訂單日期降序排序的前5個訂單。

我的要求是每次客戶端調用這個方法時,我需要返回有5個隨機順序的集合。

如何使用C#實現此目的?

回答

11

我在使用Fisher-Yates shuffle回到它做到這一點寫了TakeRandom擴展方法。這非常有效,因爲它只是麻煩隨機確定您實際想要返回的項目數量,並且保證沒有偏見。

public static IEnumerable<T> TakeRandom<T>(this IEnumerable<T> source, int count) 
{ 
    var array = source.ToArray(); 
    return ShuffleInternal(array, Math.Min(count, array.Length)).Take(count); 
} 

private static IEnumerable<T> ShuffleInternal<T>(T[] array, int count) 
{ 
    for (var n = 0; n < count; n++) 
    { 
     var k = ThreadSafeRandom.Next(n, array.Length); 
     var temp = array[n]; 
     array[n] = array[k]; 
     array[k] = temp; 
    } 

    return array; 
} 

ThreadSafeRandom的實現可以是found at the PFX team blog

+0

+1:這是一個相當完整的答案:D – 2009-12-08 12:17:22

+2

雖然它似乎在複製列表(效率?)或修改現有的(從簽名中不太明顯)。 – 2009-12-08 12:20:02

+0

@Vilk:正如我的廢話答案所證明的那樣,要避免有效地重複是很不容易的,所以我懷疑它很難提高效率。 – 2009-12-08 12:38:14

1
return collection.Where(()=>Random.Next(100) > (5/collection.Count * 100))); 
+0

與礦,你可以得到dups(也許不會得到五個) – 2009-12-08 12:17:54

2
return myList.OfType<Order>().OrderBy(o => Guid.NewGuid()).Take(5); 
+0

這'訪問'陣列的每個項目(是的,我知道你知道) – 2009-12-08 12:09:20

+0

是的。我不明白原始問題中* generics *要求的作用。如果我們只是有一個訂單列表,我們可以放棄'OfType',而上面的查詢對於linq到sql表格的列表也同樣適用。如果它是一個sql表的linq,那麼'OrderBy'子句實際上會在數據庫級別解析爲'newid()'隨機化的順序,這是完全可取的(正如你指出的那樣) – 2009-12-08 12:23:37

+0

@David:我認爲asker意味着列表因此OfType是無關緊要的。問題是NewGuid效率低於Random()(請記住,*每個Guid都是神聖的,每個Guid都很棒*。沒有實現默認的L2S [大概EF和LLBLGP等]默認的OrderBy翻譯 - 這使得這個worty成爲+1 (你應該說,在帖子中,你不應該:D) – 2009-12-08 12:40:29

4

你真的應該在數據庫中做到這一點 - 沒有意義只返回一大堆東西,只能放下所有五個。你應該修改你的問題來解釋什麼類型的數據訪問棧被涉及,這樣人們可以給出更好的答案。例如,你可以做一個ORDER BY RAND():

SELECT TOP 5 ... FROM orders 
ORDER BY RAND() 

但是那visits every row, which you don't want。如果您正在使用SQL Server [並且希望與其綁定:P],則可以使用TABLESAMPLE

If you're using LINQ to SQL, go here

編輯:這裏只是假裝這個心不是的剩下的 - 它的效率不高,因此Greg的答覆是更爲可取的,如果你想客戶端進行排序。

但是,對於完整性,以下內容粘貼到LINQPad

var orders = new[] { "a", "b", "c", "d", "e", "f" }; 
var random = new Random(); 
var result = Enumerable.Range(1,5).Select(i=>orders[random.Next(5)]) 
result.Dump(); 

編輯:蠻力回答Greg的點(是的,效率不高,或者漂亮)

var orders = new[] { "a", "b", "c", "d", "e", "f" }; 

var random = new Random(); 

int countToTake = 5; 

var taken = new List<int>(countToTake); 

var result = Enumerable.Range(1,countToTake) 
    .Select(i=>{ 
     int itemToTake; 
     do { 
      itemToTake = random.Next(orders.Length); 
     } while (taken.Contains(itemToTake)); 
     taken.Add(itemToTake); 
     return orders[itemToTake]; 
    }); 

result.Dump(); 
+0

雖然這將返回重複,這可能不是他所追求的。 – 2009-12-08 12:13:16

+0

@Greg Beech:好點,會解決(真正的問題是他的排序需要在數據庫中發生) – 2009-12-08 12:15:04

+1

投票。絕對應該在數據庫上做到這一點。 – Firestrand 2009-12-08 13:40:41