Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/291.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 将对象[]的列表分组(希望使用Linq)_C#_Linq_C# 3.0 - Fatal编程技术网

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)