zoukankan      html  css  js  c++  java
  • C# list group分组扩展,方法来源网络记录备忘

    在C#/.NET应用程序编程开发中,如何对一个泛型集合进行LINQ动态分组(GroupBy)?

    当前有一个泛型集合,一般情况下,使用LINQ进行分组都是按固定属性名称,比如有一个Customer集合,现要按固定的属性Grade进行分组统计数量,如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApp1
    {
        public class Customer
        {
            public int Id { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int Grade { get; set; }
        }
        class Program
        {
            static void Main(string[] args)
            {
                var customers = new List<Customer>
                {
                    new Customer{ Id = 1, FirstName="Rector" ,LastName = "Liu",Grade = 1},
                    new Customer{ Id = 2, FirstName="James" ,LastName = "Liu",Grade = 2},
                    new Customer{ Id = 3, FirstName="Stephen" ,LastName = "Liu",Grade = 3},
                    new Customer{ Id = 4, FirstName="Loin" ,LastName = "Liu",Grade = 2},
                    new Customer{ Id = 5, FirstName="Stephen" ,LastName = "Curry",Grade = 1},
                };
                var groupByFirstName = customers.GroupBy(x=>x.Grade)
                    .Select(x=>new {
                        x.Key,
                        Count = x.Count()
                    }).ToList();
                groupByFirstName.ForEach(x => Console.WriteLine("Key:{0},Count:{1}",x.Key, x.Count));
                Console.ReadLine();
            }
        }
    }
    

    输出结果:

    Key:1,Count:2
    Key:2,Count:2
    Key:3,Count:1

    那么,如果要在程序中依据不同情况动态对集合进行分组和统计,应该如何实现呢?

    方案一

    创建一个获取属性的方法,如下:

    private static object GetPropertyValue(object obj, string propertyName)
    {
        return obj.GetType().GetProperty(propertyName).GetValue(obj, null);
    }

    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace ConsoleApp1
    {
        public class Customer
        {
            public int Id { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int Grade { get; set; }
        }
        class Program
        {
            static void Main(string[] args)
            {
                var customers = new List<Customer>
                {
                    new Customer{ Id = 1, FirstName="Rector" ,LastName = "Liu",Grade = 1},
                    new Customer{ Id = 2, FirstName="James" ,LastName = "Liu",Grade = 2},
                    new Customer{ Id = 3, FirstName="Stephen" ,LastName = "Liu",Grade = 3},
                    new Customer{ Id = 4, FirstName="Loin" ,LastName = "Liu",Grade = 2},
                    new Customer{ Id = 5, FirstName="Stephen" ,LastName = "Curry",Grade = 1},
                };
                var groupByFirstName = customers.GroupBy(x=> GetPropertyValue(x,"LastName"))
                    .Select(x=>new {
                        x.Key,
                        Count = x.Count()
                    }).ToList();
                groupByFirstName.ForEach(x => Console.WriteLine("Key:{0},Count:{1}",x.Key, x.Count));
                Console.ReadLine();
            }
    
            private static object GetPropertyValue(object obj, string propertyName)
            {
                return obj.GetType().GetProperty(propertyName).GetValue(obj, null);
            }
        }
    }
    

    输出结果:

    Key:Liu,Count:4
    Key:Curry,Count:1

    方案二

    创建一个基于表达式树的静态方法,如下:

    private static Expression<Func<Menu, string>> GetGroupKey(string property)
    {
        var parameter = Expression.Parameter(typeof(Menu));
        var body = Expression.Property(parameter, property);
        return Expression.Lambda<Func<Menu, string>>(body, parameter);
    }
    

    调用示例:

    customers.GroupBy(GetGroupKey("LastName").Compile())
    

    完整示例:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    
    namespace ConsoleApp1
    {
        public class Customer
        {
            public int Id { get; set; }
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int Grade { get; set; }
        }
        class Program
        {
            static void Main(string[] args)
            {
                var customers = new List<Customer>
                {
                    new Customer{ Id = 1, FirstName="Rector" ,LastName = "Liu",Grade = 1},
                    new Customer{ Id = 2, FirstName="James" ,LastName = "Liu",Grade = 2},
                    new Customer{ Id = 3, FirstName="Stephen" ,LastName = "Liu",Grade = 3},
                    new Customer{ Id = 4, FirstName="Loin" ,LastName = "Liu",Grade = 2},
                    new Customer{ Id = 5, FirstName="Stephen" ,LastName = "Curry",Grade = 1},
                };
                var groupByFirstName = customers.GroupBy(GetGroupKey("LastName").Compile())
                    .Select(x=>new {
                        x.Key,
                        Count = x.Count()
                    }).ToList();
                groupByFirstName.ForEach(x => Console.WriteLine("Key:{0},Count:{1}",x.Key, x.Count));
                Console.ReadLine();
            }
    
            private static Expression<Func<Customer, string>> GetGroupKey(string property)
            {
                var parameter = Expression.Parameter(typeof(Customer));
                var body = Expression.Property(parameter, property);
                return Expression.Lambda<Func<Customer, string>>(body, parameter);
            }
        }
    }


    方案三

    创建一个支持LINQ动态分组的静态扩展方法,如下:

    public static IEnumerable<IGrouping<object, TElement>> GroupByMany<TElement>(this IEnumerable<TElement> elements, params string[] groupSelectors)
    {
        var selectors = new List<Func<TElement, object>>(groupSelectors.Length);
        selectors.AddRange(groupSelectors.Select(selector => DynamicExpression.ParseLambda(typeof (TElement), typeof (object), selector)).Select(l => (Func<TElement, object>) l.Compile()));
        return elements.GroupByMany(selectors.ToArray());
    }
    
    public static IEnumerable<IGrouping<object, TElement>> GroupByMany<TElement>(this IEnumerable<TElement> elements, params Func<TElement, object>[] groupSelectors)
    {
        if (groupSelectors.Length > 0)
        {
            Func<TElement, object> selector = groupSelectors.First();
            return elements.GroupBy(selector);
        }
        return null;
    }

    static class LinqExt
    {
    public class DGroupBy<T> : IGrouping<object[], T>
    {
    private List<T> _innerlist = new List<T>();

    private object[] _key;

    public DGroupBy(object[] key) { _key = key; }

    public object[] Key
    {
    get { return _key; }
    }

    public void Add(T value)
    {
    _innerlist.Add(value);
    }

    public IEnumerator<T> GetEnumerator()
    {
    return this._innerlist.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
    return this._innerlist.GetEnumerator();
    }
    }

    public static IEnumerable<IGrouping<object[], T>> DynamicGroupBy<T>(this IEnumerable<T> data, string[] keys)
    {
    List<DGroupBy<T>> list = new List<DGroupBy<T>>();
    foreach (var item in data.Select(x => new {
    k = keys.Select(y => x.GetType().GetProperty(y).GetValue(x, null)).ToArray(),
    v = x
    }))
    {
    DGroupBy<T> existing = list.SingleOrDefault(x => x.Key.Zip(item.k, (a, b) => a.Equals(b)).All(y => y));
    if (existing == null)
    {
    existing = new DGroupBy<T>(item.k);
    list.Add(existing);
    }
    existing.Add(item.v);
    }
    return list;
    }
    }

  • 相关阅读:
    OSGi for C/C++
    Tizen NPPlugin开发
    Trove4j
    [Tizen]某些目录下存放的东西
    OpenMobile's Application Compatibility Layer (ACL)
    params
    页面无法访问
    websevice 服务前台和后台
    SQL 创建存储过程
    UpdatePanel
  • 原文地址:https://www.cnblogs.com/yzmn/p/14092392.html
Copyright © 2011-2022 走看看