IEnumerable Group按用户指定的动态键列表

Sha*_*hab 11 .net c# asp.net-mvc ienumerable lambda

我有一个类似的课程

public class Empolyee
{
    public string Designation {get ;set;}
    public string Discipline {get ;set;}
    public int Scale {get ;set;}
    public DateTime DOB {get ;set;}
    public int Sales {get ;set;}
}
Run Code Online (Sandbox Code Playgroud)

并以可数的说法记录所有员工的记录

List<Employee> Employees;
Run Code Online (Sandbox Code Playgroud)

和一个字符串键列表

var Keys = new List<string>()
{
    "Designation",
    "Scale",
    "DOB"
};
Run Code Online (Sandbox Code Playgroud)

假设列表"Keys"的元素是用户指定的,用户可以指定no或许多关键元素.

现在我想使用列表"Keys"中指定的键对所有"Employees"进行分组,并仅选择"Keys"中指定的属性以及每个组的Sales of Sum.

在我尝试使用的3个解决方案中,以下看起来适用但无法使用它,因为不知道列表"Keys"将如何转换为匿名类型

Employees.GroupBy(e => new { e.Key1, e.Key2, ... })
    .Select(group => new {
        Key1 = group.Key.Key1,
        Key2 = group.Key.Key2,
        ...
        TotalSales = group.Select(employee => employee.Sales).Sum()
    });
Run Code Online (Sandbox Code Playgroud)

Sha*_*hab 0

对于这个问题的最终解决方案,我使用了 @jamespconnor 答案中的编码方法,但字符串作为分组键在我的实际场景中对我没有多大帮助。因此,我使用 @tim-rogers 的数组基本思想作为分组键,并使用 ArrayEqualityComparer 比较数组。

为了获取字符串集合指定的关键属性,我构建了一个静态类,例如

public static class MembersProvider
{
    public static IEnumerable<PropertyInfo> GetProperties(Type type, params string[] names)
    {
        var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty)
            .Where(pi => names.Contains(pi.Name))
            .Where(pi => pi != null)
            .AsEnumerable();
        if (names.Count() != properties.Count())
        {
            throw new InvalidOperationException("Couldn't find all properties on type " + type.Name);
        }

        return properties;
    }
}
Run Code Online (Sandbox Code Playgroud)

并更改了 @jamespconnor 的 GroupByKeys 扩展,有点像

public static class GroupByExtensions
{
    public static IEnumerable<IGrouping<object[], TValue>> GroupByKeys<TValue>(this IEnumerable<TValue> values, IEnumerable<string> keys)
    {
        var properties = MembersProvider.GetProperties(typeof(TValue), keys.ToArray());
        var comparer = new ArrayEqualityComparer<object>();


        // jamespconnor's string as key approch - off course it will need to return IEnumerable<IGrouping<string, TValue>> 
        /*return values.GroupBy(v => getters.Aggregate(
            "",
            (acc, getter) => string.Format(
                "{0}-{1}",
                acc,
                getter.Invoke(v, null).ToString()
                )
            )
        );*/

        //objects array as key approch 
        return values.GroupBy(v => properties.Select(property => property.GetValue(v, null)).ToArray(), comparer);
    }

}
Run Code Online (Sandbox Code Playgroud)

由于我还需要选择匿名类型的结果,其中每个“Key”作为其属性和一个附加的“Total”属性,但没有成功,我最终像

// get properties specified by "Keys" collection
    var properties = MembersProvider.GetProperties(typeof(Employee), Keys.ToArray());

    // Group and Select 
    var SalesSummary = Employees
        .GroupByKeys(Keys.ToArray())
        .Select(g =>
            properties.Aggregate(
                new Dictionary<string, object>() { { "TotalSales", g.Select(employee => employee.Sales).Sum() } },
                (dictionary, property) => {
                    dictionary.Add(property.Name, property.GetValue(g.FirstOrDefault(), null));
                    return dictionary;
                }   
            )
        );
Run Code Online (Sandbox Code Playgroud)