C# 将对象[]的列表分组(希望使用Linq)
假设我有一组维度相等的对象数组,如下所示:C# 将对象[]的列表分组(希望使用Linq),c#,linq,c#-3.0,C#,Linq,C# 3.0,假设我有一组维度相等的对象数组,如下所示: 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行=新列表 { 新对象[]{1,“te
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行=新列表
{
新对象[]{1,“test1”,“foo”,1},
新对象[]{1,“test1”,“foo”,2},
新对象[]{2,“test1”,“foo”,3},
新对象[]{2,“test2”,“foo”,4},
};
我想通过一个或多个“列”进行分组——在运行时动态确定哪些列。例如,按列1、2和3分组将导致三个组:
- 第1组:[1,“test1”,“foo”](包括第1行和第2行)
- 第2组:[2,“test1”,“foo”](包括第3行)
- 第3组:[2,“test2”,“foo]”(包括第4行)
当然,我可以通过某种自定义组类以及排序和迭代来实现这一点。然而,似乎我应该能够用Linq分组做得更干净。但是我的灵符让我失望了。有什么想法吗?如果您的收藏包含带有索引器的项目(例如您的
对象[]
您可以这样做
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] });
最短解决方案:
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])));
int[]列={0,1};
var seed=new[]{rows.AsEnumerable()}.AsEnumerable();//IEnumerable=group,IEnumerable=result
var result=columns.Aggregate(种子、,
(groups,nCol)=>groups.SelectMany(g=>g.GroupBy(row=>row[nCol]);
@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()));
}
}
公共类列比较程序:IEqualityComparer
{
私有只读IList\u比较索引;
公共列比较程序(参数int[]比较索引)
{
_comparedIndex=comparedIndex.ToList();
}
#地区质量比较员
公共布尔等于(对象[]x,对象[]y)
{
返回ReferenceEquals(x,y)| |(x!=null&&y!=null&&ColumnsEqual(x,y));
}
public int GetHashCode(对象[]obj)
{
返回obj==null?0:CombineColumnHashCodes(obj);
}
#端区
private bool ColumnsEqual(对象[]x,对象[]y)
{
返回_comparedIndex.All(index=>ColumnEqual(x,y,index));
}
私有布尔列相等(对象[]x,对象[]y,整数索引)
{
收益率等于(x[index],y[index]);
}
私有int组合列哈希代码(对象[]行)
{
返回\u比较索引
.选择(索引=>行[索引])
.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)
公共静态i分组groupbyindex(
这是一个数不清的来源,
参数int[]索引)
{
返回source.GroupBy(row=>row,newcolumncomarer(index));
}
//用法
row.GroupByIndexes(0、1、2)
扩展
IEnumerable
只适用于.NET 4。您需要直接在.NET 3.5中扩展List
。您不会只想xor
哈希代码。如果这样做,会增加冲突的机会。当然!很好的解决方案。ColumnComparator中有一些小错误。我编辑了您的帖子有了更正。@Matthew Whited:你是对的,这是GetHashCode
的一个不太理想的实现。不过,我想避免陷入那种混乱的讨论,所以选择了低摩擦的方法。@Tim Scott:感谢你修复了我的错误-已经很晚了:-)我注意到您在GetHashCode
中删除了空签入。我之所以包含它,是因为ColumnComparer
是一种公共类型。如果您将它设置为私有的,您可以绝对保证没有空值,那么删除它是安全的。不过,今后请不要进行风格编辑,例如在CombineColumnHashCodes
中添加局部变量。对我来说,这是多余的,我不希望它被误认为是我写的代码。谢谢。@Bryan:是的,空支票应该在那里。Resharper告诉我这永远都是假的。以前从没见过Resharper在那样的事情上犯错@Matthew Whited:你能推荐一种更健壮的方法来实现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)