Nhóm theo nhiều cột


967

Làm cách nào tôi có thể thực hiện GroupBy Nhiều cột trong LINQ

Một cái gì đó tương tự như thế này trong SQL:

SELECT * FROM <TableName> GROUP BY <Column1>,<Column2>

Làm cách nào tôi có thể chuyển đổi nó thành LINQ:

QuantityBreakdown
(
    MaterialID int,
    ProductID int,
    Quantity float
)

INSERT INTO @QuantityBreakdown (MaterialID, ProductID, Quantity)
SELECT MaterialID, ProductID, SUM(Quantity)
FROM @Transactions
GROUP BY MaterialID, ProductID

Câu trả lời:


1212

Sử dụng một loại ẩn danh.

Ví dụ

group x by new { x.Column1, x.Column2 }

29
Nếu bạn chưa quen với các loại ẩn danh, việc sử dụng từ khóa 'mới' trong ví dụ này thực sự kỳ diệu.
Chris

8
trong trường hợp mvc với nHibernate bị lỗi cho các vấn đề dll. Sự cố được giải quyết bởi GroupBy (x => new {x.Column1, x.Column2}, (key, group) => new {Key1 = key.Column1, Key2 = key.Column2, result = group.ToList ()});
Milan

Tôi nghĩ trong trường hợp này, các đối tượng mới sẽ được so sánh bằng cách tham chiếu, vì vậy không khớp - không phân nhóm.
HoGo

4
Các đối tượng gõ ẩn danh @HoGo thực hiện các phương thức Equals và GetHashCode của riêng chúng, được sử dụng khi nhóm các đối tượng.
Byron Carasco

Một chút khó khăn để hình dung cấu trúc dữ liệu đầu ra khi bạn chưa quen với Linq. Điều này có tạo ra một nhóm trong đó loại ẩn danh được sử dụng làm khóa không?
Jacques

760

Mẫu thủ tục

.GroupBy(x => new { x.Column1, x.Column2 })

Loại nào là đối tượng trả về?
mggSoft

5
@MGG_Soft đó sẽ là một loại ẩn danh
Alex

Mã này không hoạt động đối với tôi: "Công cụ khai báo loại ẩn danh không hợp lệ."
thalesfc

19
@Tom này nên hoạt động như vậy. Khi bạn bỏ qua việc đặt tên các trường thuộc loại ẩn danh C #, giả sử bạn muốn sử dụng tên của thuộc tính / trường được truy cập cuối cùng từ phép chiếu. (Vì vậy, ví dụ của bạn tương đương với Mo0gles ')
Chris Pfohl

1
tìm thấy câu trả lời của tôi Tôi cần xác định một thực thể mới (MyViewEntity) có chứa các thuộc tính Cột1 và Cột2 và loại trả về là: IEnumerable <IGrouping <MyViewEntity, MyEntity >> và Nhóm mã snip là: MyEntityList.groupBy (myEntity = myEntity = Cột1, Cột2 = myEntity.Column2});
Amir Chatrbahr 17/03/2015

468

Ok có cái này là:

var query = (from t in Transactions
             group t by new {t.MaterialID, t.ProductID}
             into grp
                    select new
                    {
                        grp.Key.MaterialID,
                        grp.Key.ProductID,
                        Quantity = grp.Sum(t => t.Quantity)
                    }).ToList();

75
+1 - Cảm ơn ví dụ toàn diện. Đoạn trích của câu trả lời khác quá ngắn và không có ngữ cảnh. Ngoài ra, bạn hiển thị một hàm tổng hợp (Tổng trong trường hợp này). Rất hữu ích. Tôi thấy việc sử dụng một hàm tổng hợp (nghĩa là MAX, MIN, SUM, v.v.) song song với việc phân nhóm là một tình huống phổ biến.
barrypicker

Ở đây: stackoverflow.com/questions/14189537/ , nó được hiển thị cho một bảng dữ liệu khi nhóm được dựa trên một cột duy nhất, có tên được biết, nhưng làm thế nào có thể được thực hiện nếu các cột dựa trên đó việc phân nhóm được thực hiện Có phải được tạo ra một cách năng động?
bg

Điều này thực sự hữu ích trong việc hiểu khái niệm nhóm và áp dụng tổng hợp trên nó.
rajibdotnet

1
Ví dụ tuyệt vời ... chỉ là những gì tôi đang tìm kiếm. Tôi thậm chí cần tổng hợp vì vậy đây là câu trả lời hoàn hảo mặc dù tôi đang tìm lambda tôi đã có đủ từ nó để giải quyết nhu cầu của mình.
Kevbo

153

Đối với nhóm theo nhiều cột, hãy thử điều này thay vì ...

GroupBy(x=> new { x.Column1, x.Column2 }, (key, group) => new 
{ 
  Key1 = key.Column1,
  Key2 = key.Column2,
  Result = group.ToList() 
});

Tương tự như cách bạn có thể thêm Cột 3, Cột 4, v.v.


4
Điều đó rất hữu ích và sẽ nhận được nhiều hơn nữa! Resultchứa tất cả các bộ dữ liệu được liên kết với tất cả các cột. Cảm ơn rất nhiều!
j00hi

1
lưu ý: Tôi đã phải sử dụng .AsEnumerable () thay vì ToList ()
GMan

1
Tuyệt vời, cảm ơn vì điều này. Đây là ví dụ của tôi. Lưu ý rằng GetFees trả về một <Query> RegistryAccountDA.GetFees (registryAccountId, fromDate, toDate) .groupBy (x => new {x.AccountId, x.FeeName}, (key, group) => new {AccountId = , ChargeName = key.FeeName, ApplicationFee = group.Sum (x => x.AppliedFee) ?? 0M}). ToList ();
Craig B

Có khả năng lấy các cột khác từ truy vấn này không được nhóm không? Nếu có mảng đối tượng, tôi muốn lấy đối tượng này được nhóm theo hai cột, nhưng lấy tất cả các thuộc tính từ đối tượng, không chỉ hai cột đó.
FrenkyB

30

Vì C # 7, bạn cũng có thể sử dụng các bộ giá trị:

group x by (x.Column1, x.Column2)

hoặc là

.GroupBy(x => (x.Column1, x.Column2))

2
Vâng, tôi nghĩ rằng bạn đang thiếu một phụ) vào cuối. Bạn sẽ không đóng ()
StuiterSlurf

Tôi đã thêm nó.
Nathan Tregillus

2
.GroupBy (x => new {x.Column1, x.Column2})
Zahidul Hồi giáo Jamy

19

C # 7.1 hoặc cao hơn sử dụng TuplesInferred tuple element names(hiện tại nó chỉ làm việc với linq to objectsvà nó không được hỗ trợ khi cây biểu hiện được yêu cầu ví dụ someIQueryable.GroupBy(...). Github vấn đề ):

// declarative query syntax
var result = 
    from x in inMemoryTable
    group x by (x.Column1, x.Column2) into g
    select (g.Key.Column1, g.Key.Column2, QuantitySum: g.Sum(x => x.Quantity));

// or method syntax
var result2 = inMemoryTable.GroupBy(x => (x.Column1, x.Column2))
    .Select(g => (g.Key.Column1, g.Key.Column2, QuantitySum: g.Sum(x => x.Quantity)));

C # 3 trở lên sử dụng anonymous types:

// declarative query syntax
var result3 = 
    from x in table
    group x by new { x.Column1, x.Column2 } into g
    select new { g.Key.Column1, g.Key.Column2, QuantitySum = g.Sum(x => x.Quantity) };

// or method syntax
var result4 = table.GroupBy(x => new { x.Column1, x.Column2 })
    .Select(g => 
      new { g.Key.Column1, g.Key.Column2 , QuantitySum= g.Sum(x => x.Quantity) });

18

Bạn cũng có thể sử dụng Tuple <> cho một nhóm được gõ mạnh.

from grouping in list.GroupBy(x => new Tuple<string,string,string>(x.Person.LastName,x.Person.FirstName,x.Person.MiddleName))
select new SummaryItem
{
    LastName = grouping.Key.Item1,
    FirstName = grouping.Key.Item2,
    MiddleName = grouping.Key.Item3,
    DayCount = grouping.Count(), 
    AmountBilled = grouping.Sum(x => x.Rate),
}

4
Lưu ý: Tạo một Tuple mới không được hỗ trợ trong Linq To Entities
đánh lừa

8

Mặc dù câu hỏi này là hỏi về nhóm theo thuộc tính lớp, nhưng nếu bạn muốn nhóm theo nhiều cột theo một đối tượng ADO (như DataTable), bạn phải gán các mục "mới" của mình cho các biến:

EnumerableRowCollection<DataRow> ClientProfiles = CurrentProfiles.AsEnumerable()
                        .Where(x => CheckProfileTypes.Contains(x.Field<object>(ProfileTypeField).ToString()));
// do other stuff, then check for dups...
                    var Dups = ClientProfiles.AsParallel()
                        .GroupBy(x => new { InterfaceID = x.Field<object>(InterfaceField).ToString(), ProfileType = x.Field<object>(ProfileTypeField).ToString() })
                        .Where(z => z.Count() > 1)
                        .Select(z => z);

1
Tôi không thể thực hiện truy vấn Linq "nhóm c theo {c.Field <String> (" Title "), c.Field <String> (" CIF ")}" và bạn đã tiết kiệm cho tôi rất nhiều thời gian !! truy vấn cuối cùng là: "nhóm c bởi new {titulo = c.Field <String> (" Title "), cif = c.Field <String> (" CIF ")}"
netadictos

4
var Results= query.GroupBy(f => new { /* add members here */  });

7
Không thêm gì vào các câu trả lời trước.
Mike Fuchs

4
.GroupBy(x => x.Column1 + " " + x.Column2)

Kết hợp với Linq.Enumerable.Aggregate()điều này thậm chí cho phép nhóm theo một số thuộc tính động : propertyValues.Aggregate((current, next) => current + " " + next).
Kai Hartmann

3
Đây là một câu trả lời tốt hơn bất cứ ai đang cung cấp tín dụng cho. Có thể có vấn đề nếu có thể có các trường hợp kết hợp trong đó cột1 được thêm vào cột2 sẽ bằng với điều tương tự cho các tình huống trong đó cột1 khác nhau ("ab" "cde" sẽ khớp với "abc" "de"). Điều đó nói rằng, đây là một giải pháp tuyệt vời nếu bạn không thể sử dụng một loại động vì bạn đang xây dựng trước lambdas sau nhóm bằng các biểu thức riêng biệt.
Brandon Barkley

3
"ab" "cde" thực sự không khớp với "abc" "de", do đó khoảng trống ở giữa.
Kai Hartmann

1
Thế còn "abc de" "" và "abc" "de" thì sao?
AlbertK

@AlbertK sẽ không hoạt động, tôi đoán vậy.
Kai Hartmann



1

Một điều cần lưu ý là bạn cần gửi một đối tượng cho các biểu thức Lambda và không thể sử dụng một thể hiện cho một lớp.

Thí dụ:

public class Key
{
    public string Prop1 { get; set; }

    public string Prop2 { get; set; }
}

Điều này sẽ biên dịch nhưng sẽ tạo một khóa cho mỗi chu kỳ .

var groupedCycles = cycles.GroupBy(x => new Key
{ 
  Prop1 = x.Column1, 
  Prop2 = x.Column2 
})

Nếu bạn không đặt tên cho các thuộc tính chính và sau đó truy xuất chúng, bạn có thể làm như thế này thay thế. Điều này sẽ GroupBychính xác và cung cấp cho bạn các thuộc tính quan trọng.

var groupedCycles = cycles.GroupBy(x => new 
{ 
  Prop1 = x.Column1, 
  Prop2= x.Column2 
})

foreach (var groupedCycle in groupedCycles)
{
    var key = new Key();
    key.Prop1 = groupedCycle.Key.Prop1;
    key.Prop2 = groupedCycle.Key.Prop2;
}

0

Đối với VBẩn danh / lambda :

query.GroupBy(Function(x) New With {Key x.Field1, Key x.Field2, Key x.FieldN })
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.