@Matthew Whited's solution is nice if you know the grouping columns up front. However, it sounds like you need to determine them at runtime. In that case, you can create an equality comparer which defines row equality for GroupBy
using a configurable column set:
rows.GroupBy(row => row, new ColumnComparer(0, 1, 2))
The comparer checks the equality of the value of each specified column. It also combines the hash codes of each value:
public class ColumnComparer : IEqualityComparer<object[]>
{
private readonly IList<int> _comparedIndexes;
public ColumnComparer(params int[] comparedIndexes)
{
_comparedIndexes = comparedIndexes.ToList();
}
#region IEqualityComparer
public bool Equals(object[] x, object[] y)
{
return ReferenceEquals(x, y) || (x != null && y != null && ColumnsEqual(x, y));
}
public int GetHashCode(object[] obj)
{
return obj == null ? 0 : CombineColumnHashCodes(obj);
}
#endregion
private bool ColumnsEqual(object[] x, object[] y)
{
return _comparedIndexes.All(index => ColumnEqual(x, y, index));
}
private bool ColumnEqual(object[] x, object[] y, int index)
{
return Equals(x[index], y[index]);
}
private int CombineColumnHashCodes(object[] row)
{
return _comparedIndexes
.Select(index => row[index])
.Aggregate(0, (hashCode, value) => hashCode ^ (value == null ? 0 : value.GetHashCode()));
}
}
If this is something you will do often, you can put it behind an extension method:
public static IGrouping<object[], object[]> GroupByIndexes(
this IEnumerable<object[]> source,
params int[] indexes)
{
return source.GroupBy(row => row, new ColumnComparer(indexes));
}
// Usage
row.GroupByIndexes(0, 1, 2)
Extending IEnumerable<object[]>
will only work with .NET 4. You would need to extend List<object[]>
directly in .NET 3.5.