I'm trying to use Linq expressions to construct a query, and am stuck trying to group by multiple columns. Say I have a basic collection:
IEnumerable<Row> collection = new Row[]
{
new Row() { Col1 = "a", Col2="x" },
new Row() { Col1 = "a", Col2="x" },
new Row() { Col1 = "a", Col2="y" },
};
I know you can group these using lambda expressions:
foreach (var grp in collection.GroupBy(item => new { item.Col1, item.Col2 }))
{
Debug.Write("Grouping by " + grp.Key.Col1 + " and " + grp.Key.Col2 + ": ");
Debug.WriteLine(grp.Count() + " rows");
}
This groups correctly as you can see:
Grouping by a and x: 2 rows
Grouping by a and y: 1 rows
But now, say I receive a collection of selectors to group against, that is passed to me as a parameter in my method, and that the entity type is generic:
void doLinq<T>(params Expression<Func<T,object>>[] selectors)
{
// linq stuff
}
Whoever's invoking the method would call like this:
doLinq<Row>(entity=>entity.Col1, entity=>entity.Col2);
How would I construct the group-by expression?
foreach (var grp in collection.GroupBy(
item => new {
// selectors??
}))
{
// grp.Key. ??
}
Edit
I updated above to hopefully clarify why I need the set of selectors.
Edit #2
Made the entity type in doLinq generic.
The solution worked for me. It involves two parts:
First part
foreach (System.Linq.IGrouping<object[], T> g in collection.GroupBy(
new Func<T, object[]>(
item => selectors.Select(sel => sel.Compile().Invoke(item)).ToArray()
),
new ColumnComparer()
)
{ ... }
Second Part
public class ColumnComparer : IEqualityComparer<object[]>
{
public bool Equals(object[] x, object[] y)
{
return Enumerable.SequenceEqual(x, y);
}
public int GetHashCode(object[] obj)
{
return (string.Join("", obj.ToArray())).GetHashCode();
}
}
This works for basic Linq, and Linq for the MySql connector. Which other Linq providers, and which expression types this works for is a whole other question ...