C#:按类型,年份和月份对付款进行分组/分组

时间:2019-08-05 20:48:10

标签: c# linq grouping

我正在执行“付款报告”,并始终将我的头靠在墙上。后端是SQL Server,付款表设置为:

PaymentId |    Date    |  Type  | Misc Fields
0          2000-08-10    cash     ...
25         2019-08-05    web CC   ...
26         2019-08-05    cash     ...
27         2019-08-05    cash     ...
28         2019-08-05    check    ...
29         2019-07-10    cash     ...

我正在尝试使用C#的LINQ读取Payment表,然后应用分组;这样结果就可以归结为:

  • 第一级:类型
  • 第二级:PaymentDate.Year
  • 第三级:PaymentDate.Month
cash
   2000
      08
         Payment 0 Details
   2019
      07
         Payment 29 Details
      08
         Payment 26
         Payment 27
check
   2019
      08
         Payment 28
web CC
   2019
      08
         Payment 25

我尝试了几种方法,但是我得到的最接近的方法是按类型/月或类型/年分组。

我偶然发现(无意开玩笑) Ms's Create a Nested Group

以下是我试图基于以下代码(类型/月份分组的结果)的基础:

var ranges = new List<String> { "Cash", "Check", "Money Order", "Web CC" };

var groupedPayments = from payment in allPayments
                                  let match = ranges.FirstOrDefault(range => range.Equals(payment.PaymentType, StringComparison.OrdinalIgnoreCase))
                                  group payment by !String.IsNullOrEmpty(match) ? match : "Other" into TypeBracket
                                  from payment2 in (
                                       from pay in TypeBracket
                                       group pay by pay.PaymentDate.Year into TypedYearBracket
                                       from payment3 in (
                                           from pay2 in TypedYearBracket
                                           group pay2 by pay2.PaymentDate.Month into MonthBracket
                                           select MonthBracket
                                       )
                                       select payment3
                                  )
                                  group payment2 by TypeBracket.Key;

控制台应用程序的简化代码

付款类别定义

public class Payment 
{
    public int PaymentId { get; set; }
        public DateTime PaymentDate { get; set; }
        public String PaymentType { get; set; }

        public Payment(int id, DateTime date, string type)
        {
            PaymentId = id;
            PaymentDate = date;
            PaymentType = type;
        }
}

主要

public class PaymentExample 
{
    private static void Main(string[] args)
        {
            List<Payment> allPayments = new List<Payment>()
            {
                new Payment(25, new DateTime(2019, 8, 5), "web CC"),
                new Payment(26, new DateTime(2019, 8, 5), "cash"),
                new Payment(27, new DateTime(2019, 8, 5), "cash"),
                new Payment(28, new DateTime(2019, 8, 5), "check"),
                new Payment(29, new DateTime(2019, 7, 10), "cash"),
                new Payment(0, new DateTime(2000, 8, 10), "cash")
            };

            var ranges = new List<String> { "Cash", "Check", "Money Order", "Web CC" };

            var groupedPayments = from payment in allPayments
                                  let match = ranges.FirstOrDefault(range => range.Equals(payment.PaymentType, StringComparison.OrdinalIgnoreCase))
                                  group payment by !String.IsNullOrEmpty(match) ? match : "Other" into TypeBracket
                                  from payment2 in (
                                       from pay in TypeBracket
                                       group pay by pay.PaymentDate.Year into TypedYearBracket
                                       from payment3 in (
                                           from pay2 in TypedYearBracket
                                           group pay2 by pay2.PaymentDate.Month into MonthBracket
                                           select MonthBracket
                                       )
                                       select payment3
                                  )
                                  group payment2 by TypeBracket.Key;

            Console.WriteLine("Hello World!");// Note I'm checking the output by using a breakpoint on this line. However, I can add further code if necessary
        }
}

***对jdweng的帖子发表评论太久了
嗯,我想我丢失了一些东西。我添加了

foreach (var x in results)
            {
                foreach (var y in x)
                {
                    Console.WriteLine("Type: {0}, Year: {1}, Month: {2}, ID: {3}", y.PaymentType, y.PaymentDate.Year, y.PaymentDate.Month, y.PaymentId);
                }

            }

到主要功能的末尾,查看打印出的内容。它似乎是按匿名类型{type,month}分组的,但结果是

Type: web CC, Year: 2019, Month: 8, ID: 25
Type: cash, Year: 2019, Month: 8, ID: 26
Type: cash, Year: 2019, Month: 8, ID: 27
Type: check, Year: 2019, Month: 8, ID: 28
Type: cash, Year: 2019, Month: 7, ID: 29

注意:类型没有组合在一起,它会完全忽略年份(在最初的示例中毫无意义,但实际数据集包含的日期可以追溯到2000年)。 2000

2 个答案:

答案 0 :(得分:1)

我想我解决了!这可能是我在这里自己设置的特定功能,但我认为我将与可能偶然遇到此问题的任何人共享该解决方案。首先,我要感谢jdweng给我一些时间,并感谢所有看过它的人,并且可能在此刻正在努力!如果您发现我的答案有问题(或可以改善);请随便发表评论或发表自己的答案!

看了一段时间(太长了,哈哈),我意识到了

   from payment3 in (
      from pay2 in TypedYearBracket
      group pay2 by pay2.PaymentDate.Month into MonthBracket
      select MonthBracket
   )
   select payment3

正在造成分组问题,并且几乎抹去了我按年份分组的尝试。

然后我意识到整个LINQ部分(group payment2 by TypeBracket.Key;的最后一个分组是将类型分组重新应用于内部组的结果。这使我意识到,代替了select payment3 ;我需要group payment3 by TypedYearBracket.Key将“年”分组重新应用于“月”子分组结果。

这最终使代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Payment> allPayments = new List<Payment>()
            {
                new Payment(25, new DateTime(2019, 8, 5), "web CC"),
                new Payment(26, new DateTime(2019, 8, 5), "cash"),
                new Payment(27, new DateTime(2019, 8, 5), "cash"),
                new Payment(28, new DateTime(2019, 8, 5), "check"),
                new Payment(29, new DateTime(2019, 7, 10), "cash"),
                new Payment(0, new DateTime(2000, 8, 10), "cash")
            };

           var ranges = new List<String> { "Cash", "Check", "Money Order", "Web CC" };

           var results = from payment in allPayments
                         let match = ranges.FirstOrDefault(range => range.Equals(payment.PaymentType, StringComparison.OrdinalIgnoreCase))
                         group payment by !String.IsNullOrEmpty(match) ? match : "Other" into TypeBracket
                         from payment2 in (
                            from pay in TypeBracket
                            group pay by pay.PaymentDate.Year into TypedYearBracket
                            from payment3 in (
                                  from pay2 in TypedYearBracket
                                  group pay2 by pay2.PaymentDate.Month into MonthBracket
                                  select MonthBracket
                            )
                            group payment3 by TypedYearBracket.Key
                         )
                         group payment2 by TypeBracket.Key;
            foreach (var x in results)
            {
                Console.WriteLine("1st Level: {0} - {1}", x.Key, x.Count());
                foreach (var y in x)
                {
                    Console.WriteLine("   2nd Level: {0} - {1}", y.Key, y.Count());
                    foreach (var z in y)
                    {
                        Console.WriteLine("      3rd Level: {0} - {1}", z.Key, z.Count());
                        foreach (var a in z)
                        {
                            Console.WriteLine("         Details: Type: {0}, Year: {1}, Month: {2}, ID: {3}", a.PaymentType, a.PaymentDate.Year, a.PaymentDate.Month, a.PaymentId);
                        }

                    }
                }
        }
    }
    public class Payment
    {
        public int PaymentId { get; set; }
        public DateTime PaymentDate { get; set; }
        public String PaymentType { get; set; }

        public Payment(int id, DateTime date, string type)
        {
            PaymentId = id;
            PaymentDate = date;
            PaymentType = type;
        }
    }
}

这给出了预期的结果:

1st Level: Web CC - 1
   2nd Level: 2019 - 1
      3rd Level: 8 - 1
         Details: Type: web CC, Year: 2019, Month: 8, ID: 25
1st Level: Cash - 2
   2nd Level: 2019 - 2
      3rd Level: 8 - 2
         Details: Type: cash, Year: 2019, Month: 8, ID: 26
         Details: Type: cash, Year: 2019, Month: 8, ID: 27
      3rd Level: 7 - 1
         Details: Type: cash, Year: 2019, Month: 7, ID: 29
   2nd Level: 2000 - 1
      3rd Level: 8 - 1
         Details: Type: cash, Year: 2000, Month: 8, ID: 0
1st Level: Check - 1
   2nd Level: 2019 - 1
      3rd Level: 8 - 1
         Details: Type: check, Year: 2019, Month: 8, ID: 28

答案 1 :(得分:-1)

尝试以下。图片显示为“现金”而不是“类型”:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> order = new List<string>() {"web CC", "cash", "check"};


            List<Payment> allPayments = new List<Payment>()
            {
                new Payment(25, new DateTime(2019, 8, 5), "web CC"),
                new Payment(26, new DateTime(2019, 8, 5), "cash"),
                new Payment(27, new DateTime(2019, 8, 5), "cash"),
                new Payment(28, new DateTime(2019, 8, 5), "check"),
                new Payment(29, new DateTime(2019, 7, 10), "cash")
            };


            var groups = allPayments
                .OrderBy(x => order.IndexOf(x.PaymentType))
                .ThenByDescending(x => x.PaymentDate)
                .GroupBy(x => new { type = x.PaymentType, month = new DateTime(x.PaymentDate.Year, x.PaymentDate.Month, 1) })
                .ToList();

            foreach(var group in groups)
            {
                foreach(Payment payment in group)
                {
                    Console.WriteLine("Type : '{0}', Year : {1}, Month : '{2}', Id : '{3}'", payment.PaymentType, payment.PaymentDate.Year.ToString(), payment.PaymentDate.Month.ToString(), payment.PaymentId);
                }
            }


        }
    }
    public class Payment
    {
        public int PaymentId { get; set; }
        public DateTime PaymentDate { get; set; }
        public String PaymentType { get; set; }

        public Payment(int id, DateTime date, string type)
        {
            PaymentId = id;
            PaymentDate = date;
            PaymentType = type;
        }
    }
}

enter image description here