Использование IEqualityComparer для аномального типа в функции LINQ GroupBy - PullRequest
0 голосов
/ 13 июня 2019

У меня есть IEnumerable из анонимного типа в результате операции соединения LINQ.Вот некоторые из значений списка:

    { 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 имеют имена столбцов таблицы.

See Excel table

из таблицы вы можете заметить, что у Джона (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 всегда будут целыми числами, поэтому я мог бы использовать приведение, но CellValue не определено, это может быть строка, целое число и т. Д.

Я могу получить IEnumerable of Anonymous Type <int, int, string, string, int>.Я в основном конвертирую CellId в int, CellIndex в int, CellValue в строку, CellDataType в строку и RowNumber в int.Но я все еще не уверен, как я могу сделать группировку.

Как я могу сгруппировать их?

Чтобы сравнить, что Id равны, мне нужно посмотреть CellIndex = 1 (что соответствуетимя столбца Id ), а затем используйте свойство CellValue (того же элемента анонимного типа), чтобы увидеть, равно ли оно.

В основном мне нужно сгруппировать по CellValue, но только для тех, которыеесть CellIndex = 1.

Есть предложения?

Ответы [ 2 ]

1 голос
/ 13 июня 2019

У вас есть коллекция ячеек, но вам нужна группировка записей. Прежде чем вы сможете получить групп записей, вам необходимо получить records . Как вы получаете записи из клеток?

Между записями и строками существует отношение один к одному, поэтому вы можете начать с группировки ячеек в строки:

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

Каждая группа теперь представляет собой строку, а элементами этой группы являются ячейки. Чтобы преобразовать эти группы в записи, необходимо преобразовать ячейки в поля записи. Как преобразовать ячейки в поля записи?

Существует соответствие между CellIndex и типом поля: «1» равно Id, «2» равно first_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
0 голосов
/ 13 июня 2019

Может быть, это вам поможет:

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

Основная идея состоит в том, чтобы сначала сгруппировать по RowNumber, а затем преобразовать ваши данные (например, вместо простого возврата всех ячеек, вы можете создать новый анонимный объект, который будетпредставлять ваш ряд) чему-то с вашим Id и, наконец, сгруппировать по Id.

...