C# 在LINQ GroupBy函数中为异常类型使用IEqualityComparer

C# 在LINQ GroupBy函数中为异常类型使用IEqualityComparer,c#,.net,linq,anonymous-types,iequalitycomparer,C#,.net,Linq,Anonymous Types,Iequalitycomparer,由于LINQ join操作,我有一个IEnumerable的匿名类型。列表中的一些值是: { CellId = 0, CellIndex = "1", CellDataType = "String", CellValue = "Id", RowNumber = 0 } { CellId = 1, CellIndex = "2", CellDataType = "String", CellValue = "first_name", RowNumber = 0 } { Cel

由于LINQ join操作,我有一个
IEnumerable
匿名类型。列表中的一些值是:

    { CellId = 0, CellIndex = "1", CellDataType = "String", CellValue = "Id", RowNumber = 0 }
    { CellId = 1, CellIndex = "2", CellDataType = "String", CellValue = "first_name", RowNumber = 0 }
    { CellId = 2, CellIndex = "3", CellDataType = "String", CellValue = "age", RowNumber = 0 }
    { CellId = 3, CellIndex = "4", CellDataType = "String", CellValue = "child_name", RowNumber = 0 }
    { CellId = 4, CellIndex = "5", CellDataType = "String", CellValue = "child_age", RowNumber = 0 }
    { CellId = 5, CellIndex = "1", CellDataType = "Number", CellValue = "1", RowNumber = 1 }
    { CellId = 6, CellIndex = "2", CellDataType = "String", CellValue = "john", RowNumber = 1 }
     .
     .
     .
(数据来自excel工作表)可以看到rowNumber=0的对象具有表的列名

从电子表格中,您可以注意到John(id=1)有3个孩子,因此我想按id分组,并有如下内容:

Id = 1
    first_name = "john", age = 30, child_name = "Andy", child_age = 4
    first_name = "john", age = 30, child_name = "Anna", child_age = 6
    first_name = "john", age = 30, child_name = "Lily", child_age = 8

Id = 2
    first_name = "Emily", age = 32, child_name = "Harry", child_age = 3
    first_name = "Emily", age = 32, child_name = "David", child_age = 3

Id = 3
    first_name = "Peter", age = 40, child_name = "Carol", child_age = 2
我想Linq GroupBy可以做到这一点。问题是:

列表的元素是匿名的
类型,其属性是泛型对象。CellId、CellIndex、RowNumber始终是整数,所以我可以使用cast,但CellValue没有定义,它可以是字符串、整数等

我可以生成匿名类型的
IEnumerable
。我基本上是将CellId转换为int,CellIndex转换为int,CellValue转换为string,CellDataType转换为string,RowNumber转换为int。但我仍然不确定如何进行分组

我如何将它们分组

要比较Id是否相等,我需要查找CellIndex=1(对应于列名Id),然后使用CellValue属性(相同匿名类型元素)查看它是否相等

基本上,我需要按CellValue分组,但只针对CellIndex=1的那些


有什么建议吗?

也许这会帮助你:

var list = new [] {
    new { CellId = 0, CellIndex = "1", CellDataType = "String", CellValue = "Id", RowNumber = 0 },
    new { CellId = 1, CellIndex = "2", CellDataType = "String", CellValue = "first_name", RowNumber = 0 },
    new { CellId = 2, CellIndex = "3", CellDataType = "String", CellValue = "age", RowNumber = 0 },
    new { CellId = 3, CellIndex = "4", CellDataType = "String", CellValue = "child_name", RowNumber = 0 },
    new { CellId = 4, CellIndex = "5", CellDataType = "String", CellValue = "child_age", RowNumber = 0 },
    new { CellId = 5, CellIndex = "1", CellDataType = "Number", CellValue = "1", RowNumber = 1 },
    new { CellId = 6, CellIndex = "2", CellDataType = "String", CellValue = "john", RowNumber = 1 },
    new { CellId = 5, CellIndex = "1", CellDataType = "Number", CellValue = "1", RowNumber = 2 },
    new { CellId = 6, CellIndex = "2", CellDataType = "String", CellValue = "john", RowNumber = 2 },
    new { CellId = 5, CellIndex = "1", CellDataType = "Number", CellValue = "2", RowNumber = 3 },
    new { CellId = 6, CellIndex = "2", CellDataType = "String", CellValue = "emily", RowNumber = 3 },
};

var result = list
    .GroupBy(x => x.RowNumber)
    //.Where(x => x.Key > 0)//in case you want to skip you header row
    .Select(x => new {  
        Id = x.SingleOrDefault(t => t.CellIndex == "1").CellValue,
        first_name = x.SingleOrDefault(t => t.CellIndex == "2")?.CellValue,
        age = x.SingleOrDefault(t => t.CellIndex == "3")?.CellValue,
        child_name = x.SingleOrDefault(t => t.CellIndex == "4")?.CellValue,
        child_age = x.SingleOrDefault(t => t.CellIndex == "5")?.CellValue
    })
    .GroupBy(x => x.Id);

其主要思想是先按
行数
分组,然后将数据(例如,您可以创建一个新的匿名对象来表示您的行,而不只是返回所有的单元格)转换为具有
Id
的数据,最后按
Id
分组。您有一个单元格集合,但您需要的是一组记录。在获取记录组之前,您需要获取记录。如何从单元格中获取记录

记录和行之间存在一对一的关系,因此您可以从将单元格分组到行开始:

var rows = joinQuery
    .GroupBy(j => j.RowNumber)
    .Where(g => g.Key != 0); // Ignore the header row
每个组现在代表一行,该组的元素是单元格。要将这些组转换为记录,需要将单元格转换为记录字段。如何将单元格转换为记录字段

CellIndex
和字段类型之间有一个映射:“1”是
Id
,“2”是
first\u name
,依此类推。因此,从单元格创建字典查找:

var lookup = rows
    .Select(g => g.ToDictionary(cell => cell.CellIndex, cell => cell.CellValue));
现在您已经在
CellIndex
上键入了一系列字典,可以利用从
CellIndex
到字段的映射。使用
GetValueOrDefault
处理字段不存在的情况:

var records = lookup.Select(l => new
{
    Id = l.GetValueOrDefault("1"),
    first_name = l.GetValueOrDefault("2"),
    age = l.GetValueOrDefault("3"),
    child_name = l.GetValueOrDefault("4"),
    child_age = l.GetValueOrDefault("5")
});
现在你有记录了。最后一步是按
Id
对它们进行分组:

var groups = records.GroupBy(r => r.Id).ToArray();

foreach (var group in groups)
{
    Console.WriteLine($"Id = {group.Key}");
    foreach (var record in group)
    {
        Console.WriteLine($"    first_name = {record.first_name}, age = {record.age}, child_name = {record.child_name}, child_age = {record.child_age}");
    }
    Console.WriteLine();
}

// Outputs:
Id = 1
    first_name = john, age = 30, child_name = Andy, child_age = 4
    first_name = john, age = 30, child_name = Anna, child_age = 6
    first_name = john, age = 30, child_name = Lily, child_age = 8

Id = 2
    first_name = Emily, age = 32, child_name = Harry, child_age = 3
    first_name = Emily, age = 32, child_name = David, child_age = 3

Id = 3
    first_name = Peter, age = 40, child_name = Carol, child_age = 2

如果不实现
IEqualityComparer
,就无法拥有相等比较器<代码>某些内容不能是匿名类型。您需要将该匿名类型映射到某个已定义的类型。为什么不定义一个类型呢?似乎你在浪费很多时间试图让匿名类型工作,而编写一个类需要五分钟。。。