DataTable runtime grouping on user defined criteria I one of my tools I use the user can define grouping for a DataTable. The criteria are known only at run-time. To achieve this I use a Dictionary&lt;string, object&gt; with a custom dictionary comparer that the GroupBy extension cosumes so that I can aggregate the groups. This works pretty well but I was wondering if there is still room for improvement. 

The number and names of columns can change. There is nothing known about the DataTable until a JSON configuration is loaded. This is an example of such a configuration from one of my unit tests.



"Columns": [
    "_nvarchar | key",
    "_datetime2",
    "_int | sum",
    "_float | avg",
    "_bit | count",
    "_money",
    "_numeric"
],


(The configuration is already being taken care of and is not a part of the review, it's just for reference to show how the columns can be configured.)

To make it simpler here's some example data:

var dataTable = new DataTable();
dataTable.Columns.Add("FirstName", typeof(string));
dataTable.Columns.Add("LastName", typeof(string));
dataTable.Columns.Add("ItemCount", typeof(int));

dataTable.Rows.Add("foo", "bar", 2);
dataTable.Rows.Add("foo", "bar", 5);
dataTable.Rows.Add("baz", "qux", 3);
dataTable.Rows.Add("foo", "bar", 2);
dataTable.Rows.Add("baz", "qux", 6);


The keyColumns are those marked with key - they are not the same as database keys and they are for aggregation later.

var keyColumns = new[] { "FirstName", "LastName" };


The implementation is very simple. Just enumerate the rows and put the name of the key column in the dictionary's key and the value of that column as the value of that dictionary item.

This is exactly the implementation I currently use:

var rowGroups = dataTable.AsEnumerable().GroupBy(x =&gt;
    keyColumns.ToDictionary(
        column =&gt; column,
        column =&gt; x[column],
        StringComparer.OrdinalIgnoreCase
    ),
    new DictionaryComparer&lt;string, object&gt;()
).ToList();


To actually be able to group the dictionaries there is also this comparer:

public class DictionaryComparer&lt;TKey, TValue&gt; : IEqualityComparer&lt;IDictionary&lt;TKey, TValue&gt;&gt;
{
    public bool Equals(IDictionary&lt;TKey, TValue&gt; x, IDictionary&lt;TKey, TValue&gt; y)
    {
        return x.All(item =&gt; y.TryGetValue(item.Key, out TValue value) &amp;&amp; item.Value.Equals(value));
    }

    // Ignore dictionary hash-code and just compare the keys and values.
    public int GetHashCode(IDictionary&lt;TKey, TValue&gt; obj) =&gt; 0;
}


And the result is:
Honestly, I am not sure if a dictionary is the right data structure for realizing composed keys. At least for me it is not directly obviously how the data are grouped. I think the main issue that confuses me is, that the grouping key contains not just the values to group by, but also the column headers which is absolutely irrelevant for grouping. Additionally I have to understand the comparer.



For your example, you could easily use a tuple which provides build-in comparison of each item:

var rowGroups = dataTable.AsEnumerable()
    .GroupBy(x =&gt; Tuple.Create(x["FirstName"], x["LastName"]))
    .ToList();


Another more flexible approach could be to create a string value to be used as grouping key:

var rowGroups = dataTable.AsEnumerable()
    .GroupBy(x =&gt; string.Join(":", keyColumns.Select(key =&gt; x[key])))
    .ToList();