【问题标题】:GroupBy with Linq to ObjectsGroupBy 与 Linq to Objects
【发布时间】:2011-10-22 22:48:04
【问题描述】:

我正在为 Linq 使用 GroupBy。我想我不能用一种表达方式做到这一点,但不知道如何解决问题。

我有以下两个课程:

public class Shipment {
  public string PortOfOrigin{get;set;}
  public string PortOfDestination{get;set;}
  public ICollection<Invoice> Invoices{get;set;}
}

public class Invoice{
  public string InvoicePeriod {get;set;}
  public decimal Amount {get;set;}
}

我有一个货件集合,它们都有一个发票集合。这是一个例子。

List<Shipment> shipments = new List<Shipment>
{
    new Shipment { PortOfOrigin = "USLOS", PortOfDestionation = "UKLON", 
        Invoices = new List<Invoice>
        {
            new Invoice{InvoicePeriod = "201106", Amount = 1000},
            new Invoice{InvoicePeriod = "201106", Amount = 2000},
            new Invoice{InvoicePeriod = "201107", Amount = 1000}
        }
    },
    new Shipment { PortOfOrigin = "USLOS", PortOfDestionation = "UKLON", 
        Invoices = new List<Invoice>
        {
            new Invoice{InvoicePeriod = "201106", Amount = 3000},
            new Invoice{InvoicePeriod = "201107", Amount = 2000}
        }
    },
    new Shipment { PortOfOrigin = "USDAL", PortOfDestionation = "UKLON", 
        Invoices = new List<Invoice>
        {
            new Invoice{InvoicePeriod = "201106", Amount = 3000}
        }
    }
};

现在我想按以下方式分组:

Shipment.PortOfOrigin、Shipment.PortOfDestionation、Shipment.Invoices.InvoicePeriod。

所以我想要这样的结果

PortOfOrigin    PortOfDestination   InvoicePeriod   Amount
------------------------------------------------------------------------
USLOS           UKLON               201106          6000
USLOS           UKLON               201107          3000
USDAL           UKLON               201106          3000

这甚至可以在我想在 Invoices.InvoicePeriod 上分组的地方进行这样的分组吗?

【问题讨论】:

  • +1 用于示例数据和您想要的结果示例。很少有这样清晰明确的问题 :-)

标签: c# group-by linq-to-objects


【解决方案1】:

更新:我已更新我的答案以包括发货数量。这要求货件有一个 ID 字段。


以下方法可以解决问题:

//first flatten the data so it's easier to manipulate
var query = from s in shipments
            from i in s.Invoices
            select new
            {
                s.ShipmentUniqueIdentifier,
                s.PortOfOrigin,
                s.PortOfDestination,
                i.InvoicePeriod,
                i.Amount
            };

//group the data as desired
var grouping = query.GroupBy(q => new { q.PortOfOrigin, q.PortOfDestination, q.InvoicePeriod });

//finally sum the amounts
var results = from g in grouping
              select new
              {
                  g.Key.PortOfOrigin,
                  g.Key.PortOfDestination,
                  g.Key.InvoicePeriod,
                  Amount = g.Select(s => s.Amount).Sum(),
                  NumberOfShipments = g.Select(s => s.ShipmentUniqueIdentifier).Distinct().Count()
              };

这是输出:

更新:正如所承诺的,这是一个完整的工作示例:

public class Shipment {
  public Guid ShipmentUniqueIdentifier{get;set;}
  public string PortOfOrigin{get;set;}
  public string PortOfDestination{get;set;}
  public ICollection<Invoice> Invoices{get;set;}
}

public class Invoice {
  public string InvoicePeriod {get;set;}
  public decimal Amount {get;set;}
}

List<Shipment> shipments = new List<Shipment>
{
    new Shipment { PortOfOrigin = "USLOS", PortOfDestination = "UKLON", ShipmentUniqueIdentifier = Guid.NewGuid(),
        Invoices = new List<Invoice>
        {
            new Invoice{InvoicePeriod = "201106", Amount = 1000},
            new Invoice{InvoicePeriod = "201106", Amount = 2000},
            new Invoice{InvoicePeriod = "201107", Amount = 1000}
        }
    },
    new Shipment { PortOfOrigin = "USLOS", PortOfDestination = "UKLON", ShipmentUniqueIdentifier = Guid.NewGuid(),
        Invoices = new List<Invoice>
        {
            new Invoice{InvoicePeriod = "201106", Amount = 3000},
            new Invoice{InvoicePeriod = "201107", Amount = 2000}
        }
    },
    new Shipment { PortOfOrigin = "USDAL", PortOfDestination = "UKLON", ShipmentUniqueIdentifier = Guid.NewGuid(), 
        Invoices = new List<Invoice>
        {
            new Invoice{InvoicePeriod = "201106", Amount = 3000}
        }
    }
};

void Main()
{
    //first flatten the data so it's easier to manipulate
    var query = from s in shipments
                from i in s.Invoices
                select new
                {
                    s.ShipmentUniqueIdentifier,
                    s.PortOfOrigin,
                    s.PortOfDestination,
                    i.InvoicePeriod,
                    i.Amount
                };

    //group the data as desired
    var grouping = query.GroupBy(q => new { q.PortOfOrigin, q.PortOfDestination, q.InvoicePeriod });

    //finally sum the amounts
    var results = from g in grouping
                  select new
                  {
                      g.Key.PortOfOrigin,
                      g.Key.PortOfDestination,
                      g.Key.InvoicePeriod,
                      Amount = g.Select(s => s.Amount).Sum(),
                      NumberOfShipments = g.Select(s => s.ShipmentUniqueIdentifier).Distinct().Count()
                  };

    //output the results
    results.Dump();
}

【讨论】:

  • 谢谢。是的,我考虑过扁平化数据,但我认为我需要创建一个新类来做到这一点,但如果我使用你的第一个 linq 语句,我就不必这样做。
  • 有没有办法获得正确数量的发货也包括在内。如果我只将 g.Count() 添加到其中,那么我将获得与每个组的发票相同的数字(因为它是扁平的)。那么我怎么才能只得到上面这个例子的出货数量呢?它应该是:2,2 和 1。而不是 3,2 和 1
  • @Magnus 我已经更新了我的答案,给出了发货数量(点赞将是一个不错的奖励;-))
  • 谢谢!是的,我能给你的奖励是最少的:)
  • 我在我的世界中尝试过它并没有用...我添加了这一行: SummarizedShipments = g.Distinct(s => s.ShipmentUniqueIdentifier).Count() 并得到了异常:无法将 lambda 表达式转换为类型“System.Collections.Generic.IEqualityComparer”,因为它不是委托类型。 !?!?!?
猜你喜欢
  • 2018-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多