Linq grouping by user class

I use linq in a DataTable (in C #) and wondered how to group across multiple fields. I found that this can be done using an anonymous class like

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

the problem is that my grouping key is dynamically determined at runtime. so instead I tried to group with a dictionary instead:

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

where GetKey(e) returns a Dictionary<string, object> . the idea is that dictionary values ​​are sorted instead of keys and values ​​of an anonymous class. my problem is that the linq query no longer works as it sees fit - it doesn't seem to group at all. my guess is that it is due to the fact that inside it must compare the grouping key for each row of the DataTable, and the dictionary keys are not considered equal just because they have the same keys and values, so each row has a different grouping key and therefore, is not aggregated.

If I am right, what is the right way to solve this problem? I tried wrapping the dictionary in the class and overriding the Equals () method, but it was never called.

+4
source share
3 answers

Why not just return the GetKey () key as a string?

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

You can create a key from the values ​​in the specified columns and enter it on one line for grouping:

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

You will need to keep track of null values, etc.

+1
source

This is cut out of the help files and something that I have not implemented but should work. The problem is that one class is needed for comparison, and it uses both ToString and GetHashCode in comparison (therefore, your idea of ​​a dictionary did not work, it does not compare dictionary elements, comparing ToString and GetHashCode). Ask GetKey to return the next class and populate the keyBag of the class with your Dictionary above:

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

This is the best logic you can try, my friend, we have a lot of research on this subject, so what kind of answer I wrote is the logic given by my professor.

-one
source

All Articles