【问题标题】:how do i pivot list of object and create new list in linq c#?我如何在 linq c# 中透视对象列表并创建新列表?
【发布时间】:2020-04-01 20:19:25
【问题描述】:

我有一个按天列出的员工工作时间数据列表。我想透视日期和时间数据并创建一个新的对象列表,其中包含员工姓名,以及一系列以各自小时为值的日期。

我的列表看起来像

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

我想创建类似 Hours 的对象的新列表

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

【问题讨论】:

  • 这里有很多类似的问题。没有标准的单线解决方案。
  • 先生,我就是这样做的。动态 d = new { Employee = group.FirstOrDefault().EmployeeName, Day1 = group.FirstOrDefault(g => g.DayNumber == 1)?.HoursPerDay, Day2 = group.FirstOrDefault(g => g.DayNumber == 2 )?.HoursPerDay, };
  • 您需要动态的日子吗?如果是这样,为什么不制定一份不同的员工列表,每个员工都有自己的日期和时间列表?
  • 我正在使用 datatables.net 插件来创建表。这只是我知道如何通过将列与返回的对象映射来填充 datatables.net jquery 中 ajax 调用的数据的一种方式。所以我选择了这种方式。你可以指导我最好的方法。我选择了这个插件,因为它重组了表格以适应小型设备。
  • 好的,所以您希望您的表格具有特定天数的“第 1 天”、“第 2 天”列?

标签: c# linq pivot-table


【解决方案1】:

使用字典。

C# 是一种强类型语言,因此类属性的可变性是有限的。似乎更适合您的情况是Dictionary&lt;string, object&gt;

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 对其进行转换时,它应该为您提供数据表中所需的数据结构。

【讨论】:

  • 似乎有很多代码可以用GroupBySelect 代替。
  • @NetMage - 对于这个初学者来说,解决方案简单直接且易于阅读。但是,让我考虑一下您对GroupBy 的见解
【解决方案2】:

如果您希望结果是静态类型的匿名(或已定义的类)对象,您将需要执行您正在执行的操作。

我会使用从每个组转换而来的增强型字典类型,以便更轻松地提取值:

//***
// 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();

还可以为每个列表成员使用更动态的对象,例如DataTableDictionaryExpandoObject,甚至可以在运行时创建一个匿名对象,尽管这是有问题的大部分时间都很有价值,因为您无法轻松访问其字段。但是,如果您不知道 Day 值如何可能,并且想要处理不同的数字,则必须使用其中一个或将日期字段替换为某种集合(例如数组、列表、或字典)。

【讨论】:

  • 这个孩子想要的只是一个他可以在 datatables.js 中读取的数据结构。这似乎有点过度设计的需要。 XD。不过看到你的方法还是很有趣的。
  • @PaulCarlton 从我的回答所依据的原始问题中并不清楚:) 或者我认为一切都是钉子,扩展方法是我选择的锤子。我等不及要延期了。
  • 哈! :) 对我来说,扩展有它的位置,只要它不妨碍模拟测试,扩展非常适合简化 dto 并为重复性任务添加简洁的语法糖。
猜你喜欢
  • 1970-01-01
  • 2021-03-02
  • 2019-03-17
  • 2021-10-03
  • 2022-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多