Linq : GroupBy, 합계 및 개수


133

제품 컬렉션이 있습니다

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

답변:


285

첫 번째 "샘플 데이터 결과"가 어디에서 오는지 이해하지 못하지만 콘솔 앱의 문제 는 각 그룹의 각 항목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유형으로 변경해야한다고 제안합니다. 텍스트가 아닌 데이터에 대해 문자열 속성을 사용하는 이유는 무엇입니까?


좋아, 내 콘솔 앱이 작동 중입니다. First ()를 사용하고 SelectMany를 제외하도록 알려주십시오. ResultLine은 실제로 ViewModel입니다. 가격은 통화 기호로 표시됩니다. 그렇기 때문에 문자열이어야합니다. 그러나 수량을 int로 변경할 수 있습니다. 이것이 내 웹 사이트에 도움이되는지 지금 알 수 있습니다. 내가 알려 주마.
ThdK

6
@ThdK : 아니요, Price십진수로 유지 한 다음 형식을 변경해야합니다. 데이터 표현을 깨끗하게 유지하고 마지막 순간에만 프레젠테이션보기로 변경하십시오.
Jon Skeet

4
제품 코드 및 이름별로 그룹화하지 않는 이유는 무엇입니까? 뭐 그런 : .GroupBy (L => 새로운 {l.ProductCode, l.Name}) 사용 제품 이름 = c.Key.Name,
키릴 Bestemyanov

@ KirillBestemyanov : 네, 다른 옵션입니다.
Jon Skeet

좋아, 내 컬렉션이 실제로 실제 컬렉션을 둘러싼 래퍼 인 것 같아 .. 날 쏴 .. 오늘 Linq를 연습 해
봤어

27

다음 쿼리가 작동합니다. 대신 각 그룹을 사용하여 선택을 수행합니다 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(),
              };

2

때로는 일부 필드를 선택 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();

1
왜 가끔 FirstOrDefault() or singleOrDefault ()` 를 사용 해야하는지 설명해 주시겠습니까?
Shanteshwar Inde

@ShanteshwarInde First () 및 FirstOrDefault ()는 일련의 첫 번째 객체를 가져 오는 반면 Single () 및 SingleOrDefault ()는 결과에서 1 만 기대합니다. Single () 및 SingleOrDefault ()가 결과 집합에 또는 제공된 인수의 결과로 둘 이상의 객체가있는 경우 예외를 발생시킵니다. 사용시, 당신은 원할 때 전자를 사용합니다. 아마도 일련의 샘플과 다른 객체는 중요하지 않습니다. 반면, 하나의 객체 만 기대하고 하나 이상의 결과가있는 경우 무언가를 수행하는 경우 후자를 사용합니다 오류 로그와 같이.
Kristianne Nerona
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.