2010-11-09 62 views
4

我有一個類的分組與過濾...LINQ查詢的特殊羣體

class Document 
{ 
    public int GroupID { get; set; } 
    public bool Valid { get; set; } 
    // more 
} 

...和實例的列表:IEnumerable<Document> documents。在通過此列表逐個對象地運行這些文檔的第一步中,這些文檔已被驗證,這意味着:對於某些對象,屬性Valid將是true,對於列表中的其他對象,將爲false

現在,在第二步驟中我必須執行以下操作:

  • 如果每文檔組標誌Valid(由所有文檔具有相同GroupID定義)的至少一個文檔是false然後設置Validfalse爲該組的所有文檔。

要做到這一點,我創建至今下面的代碼片段:

var q = from d in documents 
     group d by d.GroupID; 
// q is now of type IEnumerable<IGrouping<int, Document>> 

foreach (var dg in q) // dg = "document group", of type IGrouping<int, Document> 
{ 
    if (dg.Any(d => !d.Valid)) 
    { 
     foreach (var d in dg) 
      d.Valid = false; 
    } 
} 

我相信,這不會是我想要的東西(我沒有測試到現在爲止,雖然),但不是很有效。

問:是否有改善這種代碼,尤其是移動Any方法的語義外foreach循環「莫名其妙」到初始LINQ查詢的方式,使q僅代表至少有一個組無效的文件? (另外我顯然對只有一個元素的組不感興趣,因爲這些組只有一個元素,所以這些組也可以被過濾掉。)

感謝您提前提出建議!

回答

4

我想這你想要做什麼:

var q = from d in documents 
     group d by d.GroupID into g 
     where g.Count() > 1 && g.Any(d => !d.Valid) 
     select g; 

foreach (var dg in q) 
{ 
    foreach (var d in dg) 
    { 
     d.Valid = false; 
    } 
} 

用流利的語法頂端部分看起來像:

var q = documents.GroupBy(d => d.GroupID) 
      .Where(g => g.Count() > 1 && g.Any(d => !d.Valid)); 
+0

是的,我想,這正是我一直在尋找的!非常自然和直接的表述。謝謝! – Slauma 2010-11-09 17:06:32

1

如果您只是想將有效標誌設置爲false,如果其中一個文檔不是有效的,則可以嘗試抓取無效的GroupId列表,然後設置所有共享組的文檔與他們是無效的。示例代碼如下:

//Find the invalid GroupIds. 
var invalidIds = documents.Where(d => !d.IsValid).Select(p => p.GroupId).Distinct(); 

//invalidIds now holds the bad groupIds. 
//So we can find out if each document's GroupId is an invalid one, and if it is, mark it as invalid. 
documents.Where(d => invalidIds.Contains(d.GroupId)).ToList().ForEach(p => p.IsValid = false); 

希望這會有所幫助。

+0

看起來相當複雜的!我有編譯問題,因爲擴展方法ForEach不知道。我是否需要額外的程序集或命名空間?或者它是一個.NET 4.0功能? (我必須留在.NET 3.5 SP1這個項目中。) – Slauma 2010-11-09 17:02:19

+0

@Slauma - 這一切都取決於你的文檔對象是什麼類型的集合。如果它是一個列表,那麼foreach擴展方法應該工作。如果它是IEnumberable 或其他類型的集合,則可能沒有可用的foreach擴展方法。你需要做documents.Where(d => invalidIds.Contains(d.GroupId))。ToList()。ForEach(p => p.IsValid = false); – kevev22 2010-11-09 17:15:23

+0

@Slauma - 我更新了我的代碼示例。我忘了你需要首先投入列表。 – 2010-11-09 17:16:44