[C#] Linq : GroupBy, 합계 및 개수

제품 컬렉션이 있습니다

public class Product {

   public Product() { }

   public string ProductCode {get; set;}
   public decimal Price {get; set; }
   public string Name {get; set;}
}

이제 제품 코드를 기준으로 컬렉션을 그룹화하고 이름, 각 코드의 제품 또는 각 제품의 총 가격이 포함 된 개체를 반환하려고합니다.

public class ResultLine{

   public ResultLine() { }

   public string ProductName {get; set;}
   public string Price {get; set; }
   public string Quantity {get; set;}
}

그래서 GroupBy를 사용하여 ProductCode별로 그룹화 한 다음 합계를 계산하고 각 제품 코드의 레코드 수를 계산합니다.

이것이 내가 지금까지 가진 것입니다.

List<Product> Lines = LoadProducts();    
List<ResultLine> result = Lines
                .GroupBy(l => l.ProductCode)
                .SelectMany(cl => cl.Select(
                    csLine => new ResultLine
                    {
                        ProductName =csLine.Name,
                        Quantity = cl.Count().ToString(),
                        Price = cl.Sum(c => c.Price).ToString(),
                    })).ToList<ResultLine>();

어떤 이유로 합계는 올바르게 수행되지만 카운트는 항상 1입니다.

삼페 데이터 :

List<CartLine> Lines = new List<CartLine>();
            Lines.Add(new CartLine() { ProductCode = "p1", Price = 6.5M, Name = "Product1" });
            Lines.Add(new CartLine() { ProductCode = "p1", Price = 6.5M, Name = "Product1" });
            Lines.Add(new CartLine() { ProductCode = "p2", Price = 12M, Name = "Product2" });

샘플 데이터 결과 :

Product1: count 1   - Price:13 (2x6.5)
Product2: count 1   - Price:12 (1x12)

제품 1은 개수 = 2 여야합니다!

간단한 콘솔 응용 프로그램에서 이것을 시뮬레이션하려고 시도했지만 다음과 같은 결과가 나타납니다.

Product1: count 2   - Price:13 (2x6.5)
Product1: count 2   - Price:13 (2x6.5)
Product2: count 1   - Price:12 (1x12)

Product1 : 한 번만 나열해야합니다 … 위의 코드는 pastebin에서 찾을 수 있습니다. http://pastebin.com/cNHTBSie



답변

첫 번째 “샘플 데이터 결과”가 어디에서 오는지 이해하지 못하지만 콘솔 앱의 문제 는 각 그룹의 각 항목SelectMany 을 보는 데 사용 한다는 것 입니다.

난 당신이 원하는 것 같아요 :

List<ResultLine> result = Lines
    .GroupBy(l => l.ProductCode)
    .Select(cl => new ResultLine
            {
                ProductName = cl.First().Name,
                Quantity = cl.Count().ToString(),
                Price = cl.Sum(c => c.Price).ToString(),
            }).ToList();

사용 First()제품 이름을 얻기 위해 여기에 같은 제품 코드와 모든 제품이 동일한 제품 이름이 있다고 가정합니다. 주석에서 언급했듯이 제품 코드뿐만 아니라 제품 이름별로 그룹화 할 수 있습니다. 이는 주어진 코드에서 이름이 항상 동일하지만 EF에서 더 나은 SQL을 생성하는 경우 동일한 결과를 제공합니다.

또한 QuantityPrice속성을 각각 intdecimal유형으로 변경해야한다고 제안합니다. 텍스트가 아닌 데이터에 대해 문자열 속성을 사용하는 이유는 무엇입니까?


답변

다음 쿼리가 작동합니다. 대신 각 그룹을 사용하여 선택을 수행합니다 SelectMany. SelectMany각 컬렉션의 각 요소에서 작동합니다. 예를 들어 쿼리에서 2 개의 컬렉션 결과가 있습니다. SelectMany각 컬렉션 대신 총 3 개의 모든 결과를 가져옵니다. 다음 코드 IGrouping는 선택 부분 에서 각각 작동 하여 집계 작업이 올바르게 작동하도록합니다.

var results = from line in Lines
              group line by line.ProductCode into g
              select new ResultLine {
                ProductName = g.First().Name,
                Price = g.Sum(pc => pc.Price).ToString(),
                Quantity = g.Count().ToString(),
              };


답변

때로는 일부 필드를 선택 FirstOrDefault()하거나 singleOrDefault()아래 쿼리를 사용할 수 있습니다.

List<ResultLine> result = Lines
    .GroupBy(l => l.ProductCode)
    .Select(cl => new Models.ResultLine
            {
                ProductName = cl.select(x=>x.Name).FirstOrDefault(),
                Quantity = cl.Count().ToString(),
                Price = cl.Sum(c => c.Price).ToString(),
            }).ToList();


답변