简体   繁体   English

在LINQ GroupBy函数中将IEqualityComparer用于匿名类型

[英]Using a IEqualityComparer for Anoymous Type in a LINQ GroupBy function

I have a IEnumerable of anonymous type as result of a LINQ join operation. 由于LINQ连接操作,我有一个匿名类型IEnumerable Some of the values of the list are: 该列表的一些值是:

    { 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 }
     .
     .
     .

(The data is coming from a excel sheet) you can see that the objects with rowNumber = 0 have the column names of the table. (数据来自excel表),您可以看到rowNumber = 0的对象具有表的列名。

见Excel表格

from the spreadsheet you can notice that John (id=1) has 3 children, so I would like to group by id and have something like: 从电子表格中,您可以注意到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

I assume that Linq GroupBy can do this. 我认为Linq GroupBy可以做到这一点。 The problem is: 问题是:

The elements of the list are of anonymous type and its properties are generic objects. 列表的元素是匿名 类型 ,其属性是通用对象。 CellId, CellIndex, RowNumber will always be integers so I could use cast, but CellValue is not defined, it could be string, integer, etc. CellId,CellIndex,RowNumber将始终是整数,因此我可以使用强制转换,但未定义CellValue,它可以是字符串,整数等。

I can produce an IEnumerable of Anonymous Type <int, int, string, string, int> . 我可以产生一个IEnumerable of Anonymous Type <int, int, string, string, int> I am basically converting CellId to int, CellIndex to int, CellValue to string, CellDataType to string and RowNumber to int. 我基本上是将CellId转换为int,将CellIndex转换为int,将CellValue转换为字符串,将CellDataType转换为字符串,将RowNumber转换为int。 But I am not sure still how can I do the grouping. 但是我仍然不确定如何进行分组。

How can I group them? 如何将它们分组?

To compare that the Id are equals I need to look for CellIndex = 1 (which corresponds to the column name Id ) and then use the CellValue property (of the same anonymous type element) to see if it is equal. 为了比较Id是否相等,我需要查找CellIndex = 1(对应于列名Id ),然后使用CellValue属性(具有相同的匿名类型元素)来查看其是否相等。

Basically I need to group by CellValue but only for those that have a CellIndex = 1. 基本上,我需要按CellValue分组,但仅针对那些具有CellIndex = 1的分组。

Any suggestions? 有什么建议么?

You have a collection of cells, but what you want is a grouping of records. 您有一个单元格集合,但是想要的是一组记录。 Before you can get groups of records, you need to get records . 在获得记录之前,您需要获取记录 How do you get records from cells? 您如何从单元格获取记录?

There's a one-to-one relationship between records and rows, so you can start by grouping the cells into rows: 记录与行之间存在一对一的关系,因此您可以从将单元格分组为行开始:

var rows = joinQuery
    .GroupBy(j => j.RowNumber)
    .Where(g => g.Key != 0); // Ignore the header row

Each group now represents a row, and the elements of that group are the cells. 现在,每个组代表一行,并且该组的元素是单元格。 To convert those groups into records, you need to convert the cells into record fields. 要将这些组转换为记录,您需要将单元格转换为记录字段。 How do you convert cells into record fields? 您如何将单元格转换为记录字段?

There's a mapping between CellIndex and the kind of field: "1" is Id , "2" is first_name , and so on. CellIndex和字段类型之间存在映射:“ 1”是Id ,“ 2”是first_name ,依此类推。 So create a dictionary lookup from the cells: 因此,从单元格创建字典查找:

var lookup = rows
    .Select(g => g.ToDictionary(cell => cell.CellIndex, cell => cell.CellValue));

Now that you've got a sequence of dictionaries keyed on CellIndex , take advantage of the mapping from CellIndex to fields. 现在您已经有了在CellIndex键入的字典序列,可以利用从CellIndex到字段的映射。 Handle the case where the field doesn't exist by using GetValueOrDefault : 使用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")
});

Now you have records. 现在您有了记录。 Last step is to group them by Id : 最后一步是按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

Maybe this will help you: 也许这会帮助您:

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);

The main idea is to group by RowNumber first then transform your data(eg instead of just returning all cells you can create a new anonymous object which will represent your row) to something with your Id and finally group by Id . 主要思想是RowNumber分组,然后将您的数据转换(例如,不只是返回所有单元格,您还可以创建一个新的匿名对象来表示您的行)到带有您的Id ,最后按Id分组。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM