假设我有一组相同维度的对象数组,如下所示:
var rows = new List<object[]>
{
new object[] {1, "test1", "foo", 1},
new object[] {1, "test1", "foo", 2},
new object[] {2, "test1", "foo", 3},
new object[] {2, "test2", "foo", 4},
};
我希望按一个或多个“列”分组 - 哪些是在运行时动态确定的。例如,按列1,2和3进行分组将产生三组:
当然,我可以通过某种自定义组类以及排序和迭代来实现这一点。但是,看起来我应该能够用Linq分组来做得更干净。但我的Linq-fu让我失望了。有什么想法吗?
答案 0 :(得分:2)
@Matthew Whited的解决方案很好,如果你事先知道分组列。但是,听起来你需要在运行时确定它们。在这种情况下,您可以创建一个相等比较器,它使用可配置的列集定义GroupBy
的行相等性:
rows.GroupBy(row => row, new ColumnComparer(0, 1, 2))
比较器检查每个指定列的值是否相等。它还结合了每个值的哈希码:
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()));
}
}
如果您经常这样做,可以将其置于扩展方法之后:
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)
扩展IEnumerable<object[]>
仅适用于.NET 4.您需要在.NET 3.5中直接扩展List<object[]>
。
答案 1 :(得分:1)
如果您的收藏中包含带有索引器的项目(例如您的object[]
,您可以这样做......
var byColumn = 3;
var rows = new List<object[]>
{
new object[] {1, "test1", "foo", 1},
new object[] {1, "test1", "foo", 2},
new object[] {2, "test1", "foo", 3},
new object[] {2, "test2", "foo", 4},
};
var grouped = rows.GroupBy(k => k[byColumn]);
var otherGrouped = rows.GroupBy(k => new { k1 = k[1], k2 = k[2] });
...如果您不喜欢上面的静态集,您也可以直接在LINQ中做一些更有趣的事情。这将假设您的HashCodes将适用于Equals评估。 注意,您可能只想撰写IEqualityComparer<T>
var cols = new[] { 1, 2};
var grouped = rows.GroupBy(
row => cols.Select(col => row[col])
.Aggregate(
97654321,
(a, v) => (v.GetHashCode() * 12356789) ^ a));
foreach (var keyed in grouped)
{
Console.WriteLine(keyed.Key);
foreach (var value in keyed)
Console.WriteLine("{0}|{1}|{2}|{3}", value);
}
答案 2 :(得分:0)
最短的解决方案:
int[] columns = { 0, 1 };
var seed = new[] { rows.AsEnumerable() }.AsEnumerable(); // IEnumerable<object[]> = group, IEnumerable<group> = result
var result = columns.Aggregate(seed,
(groups, nCol) => groups.SelectMany(g => g.GroupBy(row => row[nCol])));