2009-10-15 53 views
4

我在DataTable上使用linq(在C#中),並想知道如何按多個字段進行分組。我發現它可以用匿名課程完成,例如linq按自定義類分組

var a = dt.AsEnumerable().GroupBy(e => new { name = e["Name"] }) 

問題是,我的分組鍵是在運行時動態確定的。所以我反而試圖通過一個字典,而不是分組:

var a = dt.AsEnumerable().GroupBy(e => GetKey(e)) 

其中GetKey(e)返回Dictionary<string, object>。這個想法是字典值取代了匿名類的鍵和值。我的問題是,linq查詢不再按要求工作 - 它似乎根本沒有做任何分組。我的直覺是,這是因爲它在內部必須比較每個DataTable行的分組鍵,而字典鍵因爲它們具有相同的鍵和值而不被視爲相同,所以每行都有不同的分組鍵,因此不是聚集。

如果我是對的,那麼解決這個問題的正確方法是什麼?我嘗試在類中包裝字典,並重寫Equals()方法,但它從未被調用過。

回答

1

爲什麼不只是讓GetKey()以字符串形式返回鍵?

var a = dt.AsEnumerable().GroupBy(e => new { name = e[GetKey(e)] }); 

可以在指定的列創建從價值的關鍵,並使其成爲一個字符串組:

var keyDictionary = new Dictionary<string, IEnumerable<string>>(); 
keyDictionary.Add("Table1", new List<string> {"Group", "Position"}); 

var dt = new DataTable("Table1"); 
dt.Columns.AddRange(new [] { new DataColumn("Id", typeof(int)), new DataColumn("Group", typeof(string)), new DataColumn("Position", typeof(string)), new DataColumn("Name", typeof(string))}); 
var rowItemArrays = new [] { new object[] { 1, "Alpha", "Left", "Bob" }, new object[] { 2, "Alpha", "Right", "Mary"}, new object[] { 3, "Beta", "Right", "Bill"}, new object[] { 4, "Alpha", "Right", "Larry"}}; 
rowItemArrays.ToList().ForEach(i => dt.Rows.Add(i)); 

Func<DataRow, string> GetKeys = (dataRow) => string.Join("", keyDictionary[dataRow.Table.TableName].Select(key => dataRow[key].ToString()).ToArray()); 

var a = dt.AsEnumerable().GroupBy(GetKeys); 

你不得不注意空值等..

+0

想到這一點,我沒有去用它,因爲我在迭代linq查詢結果時使用了字典數據。我想我仍然可以將所有值附加到單個字符串中,並將其解析爲鍵/值對。 – toasteroven 2009-10-15 22:16:30

+0

好的,我添加了一些適合我的東西。 – 2009-10-16 00:02:49

1

這是從幫助文件和我沒有實現,但應該工作cribbed。問題是你需要一個類來進行比較,它在比較中同時使用了ToString和GetHashCode(這就是爲什麼你的字典思想不起作用,它沒有比較字典的元素,它比較了ToString和它的GetHashCode)。有信息getKey與字典從上面返回下面的類和填充密鑰包類:

class PortableKey 
{ 
    public Dictionary<string, object> keyBag { get; set; } 

    public PortableKey(Dictionary<string, object> Keys) 
    { 
     this.keyBag = Keys; 
    } 

    public override bool Equals(object obj) 
    { 
     PortableKey other = (PortableKey)obj; 
     foreach (KeyValuePair<string, object> key in keyBag) 
     { 
      if (other.keyBag[key.Key] != key.Value) return false; 
     } 
     return true; 
    } 

    public override int GetHashCode() 
    { 
     // hashCodes is an array of integers represented as strings. { "1", "4", etc. } 
     string[] hashCodes = keyBag.Select(k => k.Value.GetHashCode().ToString()).ToArray(); 
     // hash is the Hash Codes all joined in a single string. "1,4,etc." 
     string hash = string.Join(",", hashCodes); 
     // returns a single hash code for the combined hash. 
     // Note, this is not guaranteed unique, nor is it intended to be so. 
     return hash.GetHashCode(); 
    } 
    public override string ToString() 
    { 
     string[] values = keyBag.Select(k => k.Value.ToString()).ToArray(); 
     return string.Join(",", values); 
    } 
} 
-1
var keyDictionary = new Dictionary<string, IEnumerable<string>>(); 
keyDictionary.Add("Table1", new List<string> {"Group", "Position"}); 

var dt = new DataTable("Table1"); 
dt.Columns.AddRange(new [] { new DataColumn("Id", typeof(int)), new DataColumn("Group", typeof(string)), new DataColumn("Position", typeof(string)), new DataColumn("Name", typeof(string))}); 
var rowItemArrays = new [] { new object[] { 1, "Alpha", "Left", "Bob" }, new object[] { 2, "Alpha", "Right", "Mary"}, new object[] { 3, "Beta", "Right", "Bill"}, new object[] { 4, "Alpha", "Right", "Larry"}}; 
rowItemArrays.ToList().ForEach(i => dt.Rows.Add(i)); 

Func<DataRow, string> GetKeys = (dataRow) => string.Join("", keyDictionary[dataRow.Table.TableName].Select(key => dataRow[key].ToString()).ToArray()); 

var a = dt.AsEnumerable().GroupBy(GetKeys); 

這是你可以試試我的朋友最好的邏輯,我們有很多關於這個研究中,還等什麼答案我寫的是由我的教授給出的邏輯