Как мне развернуть список объектов и создать новый список в linq c#? - PullRequest
0 голосов
/ 01 апреля 2020

У меня есть список данных о рабочих часах сотрудников по дням. Я хочу повернуть данные о днях и часах и создать новый список объектов с именем сотрудника и серией дней с соответствующими часами в качестве значения.

Мой список выглядит как

List<EmployeeHour> employeeHours = new List<EmployeeHour>
{
new EmployeeHour{Id = 1, EmployeeName = "Emp1", Day = 1, Hours = 6},
new EmployeeHour{Id = 1, EmployeeName = "Emp1", Day = 2, Hours = 8},
new EmployeeHour{Id = 2, EmployeeName = "Emp2", Day = 1, Hours = 6},
new EmployeeHour{Id = 2, EmployeeName = "Emp2", Day = 2, Hours = 8}
};

Я хочу создать новый список объекта, где Часы выглядят как

new {
   Id = 1,
   EmplyeeName = Emp1,
   Day1 = 6,
   Day2 = 8
},
new {
   Id = 2,
   EmplyeeName = Emp2,
   Day1 = 6,
   Day2 = 8
}

Вот как я это сделал. Есть ли лучшее решение?

  dynamic d = new
                    {
                        Employee = group.FirstOrDefault().EmployeeName,
                        Day1 = group.FirstOrDefault(g => g.DayNumber == 1)?.HoursPerDay,
                        Day2 = group.FirstOrDefault(g => g.DayNumber == 2)?.HoursPerDay,
                        Day3 = group.FirstOrDefault(g => g.DayNumber == 3)?.HoursPerDay,
                        Day4 = group.FirstOrDefault(g => g.DayNumber == 4)?.HoursPerDay,
                        Day5 = group.FirstOrDefault(g => g.DayNumber == 5)?.HoursPerDay,
                        Day6 = group.FirstOrDefault(g => g.DayNumber == 6)?.HoursPerDay,
                        Day7 = group.FirstOrDefault(g => g.DayNumber == 7)?.HoursPerDay
                    };
list.Add(d);

Ответы [ 2 ]

1 голос
/ 02 апреля 2020

Если вы хотите, чтобы результатом был статически типизированный анонимный (или определенный класс) объект, вам нужно будет сделать что-то похожее на то, что вы делаете.

Я бы использовал расширенный тип словаря, преобразованный из каждой группы. чтобы упростить извлечение значений:

//***
// Enhanced Dictionary that returns default(TValue) for missing values
//***
public class NullDictionary<TKey, TValue> : Dictionary<TKey, TValue> {
    public NullDictionary(IDictionary<TKey, TValue> d) : base() {
        foreach (var kvp in d)
            Add(kvp.Key, kvp.Value);
    }
    public NullDictionary() : base() { }

    public new TValue this[TKey key]
    {
        get
        {
            TryGetValue(key, out var val);
            return val;
        }
        set
        {
            base[key] = value;
        }
    }
}

С помощью метода расширения, упрощающего создание:

public static class DictExt {
    public static NullDictionary<TKey, TValue> ToNullDictionary<T, TKey, TValue>(this IEnumerable<T> src, Func<T, TKey> keyFn, Func<T, TValue> valFn) {
        var nd = new NullDictionary<TKey, TValue>();
        foreach (var s in src)
            nd.Add(keyFn(s), valFn(s));
        return nd;
    }
}

Теперь вы можете поворачивать данные с помощью GroupBy и извлекать анонимные данные. объекты:

var ans = employeeHours.GroupBy(eh => new { eh.Id, eh.EmployeeName }, eh => new { Day = $"Day{eh.Day}", eh.Hours })
                         .Select(pg => {
                             var pd = pg.ToNullDictionary(p => p.Day, p => (int?)p.Hours);
                             return new {
                                pg.Key.Id,
                                pg.Key.EmployeeName,
                                Day1 = pd["Day1"],
                                Day2 = pd["Day2"],
                                Day3 = pd["Day3"],
                                Day4 = pd["Day4"],
                                Day5 = pd["Day5"],
                                Day6 = pd["Day6"],
                                Day7 = pd["Day7"],
                            };
                         })
                         .ToList();

Также можно использовать более динамический c объект, такой как DataTable, или Dictionary или ExpandoObject для каждого члена списка, или даже для создания анонимный объект во время выполнения, хотя в большинстве случаев это сомнительная ценность, поскольку вы не можете легко получить доступ к его полям. Однако, если вы не знали, как могут быть возможны значения Day, и хотите обрабатывать изменяющееся число, вы должны использовать одно из них или заменить вместо этого поля дня коллекцией некоторого вида (например, массив, список, или словарь).

1 голос
/ 02 апреля 2020

Используйте словарь.

C# - язык строго типизированный, поэтому изменчивость свойств класса ограничена. В вашем случае лучше подходит Dictionary<string, object>:

var dictionaries = employeeHours.Select(x => new {x.Id, x.EmployeeName})
    .Distinct()
    .Select(x => new Dictionary<string, object>()
    {
        {"Id", x.Id},
        {"EmployeeName", x.EmployeeName}
    })
    .ToList();

foreach (var dict in dictionaries)
{
    var id = (int)dict["Id"];
    foreach (var dh in employeeHours.Where(x => x.Id == id))
    {
        if (!dict.ContainsKey("Day" + dh.Day))
        {
            dict["Day" + dh.Day] = dh.Hours;
        }
    }
}

. Этот код даст вам словарь переменной длины, а когда он преобразуется с использованием JSON, он должен дать вам необходимая структура данных в таблицах данных.

...