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을 생성하는 경우 동일한 결과를 제공합니다.

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


좋아, 내 콘솔 앱이 작동 중입니다. 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.