Kelompokkan Dengan Banyak Kolom

967

Bagaimana saya bisa melakukan GroupBy Beberapa Kolom di LINQ

Sesuatu yang mirip dengan ini di SQL:

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

Bagaimana saya bisa mengonversikan ini ke 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
Sreedhar
sumber

Jawaban:

1213

Gunakan tipe anonim.

Misalnya

group x by new { x.Column1, x.Column2 }
leppie
sumber
29
Jika Anda baru mengenal pengelompokan dengan tipe anonim, penggunaan kata kunci 'baru' dalam contoh ini merupakan keajaiban.
Chris
8
dalam kasus mvc dengan nHibernate mendapatkan kesalahan untuk masalah dll. Masalah diselesaikan oleh GroupBy (x => baru {x.Column1, x.Column2}, (kunci, grup) => baru {Key1 = key.Column1, Key2 = key.Column2, Hasil = group.ToList ()});
Milan
Saya pikir dalam hal ini objek baru akan dibandingkan dengan referensi, jadi tidak ada yang cocok - tidak ada pengelompokan.
HoGo
4
@HoGo objek yang diketik anonim menerapkan metode Equals dan GetHashCode mereka sendiri yang digunakan saat mengelompokkan objek.
Byron Carasco
Agak sulit untuk memvisualisasikan struktur data output ketika Anda baru mengenal Linq. Apakah ini membuat pengelompokan di mana jenis anonim digunakan sebagai kunci?
Jacques
760

Sampel prosedural

.GroupBy(x => new { x.Column1, x.Column2 })
Mo0gles
sumber
Apa jenis objek yang dikembalikan?
mggSoft
5
@MGG_Soft yang merupakan tipe anonim
Alex
Kode ini tidak berfungsi untuk saya: "Deklarator jenis anonim tidak valid."
thalesfc
19
@ Tom ini harus bekerja seperti ini. Ketika Anda melewatkan penamaan bidang tipe anonim C # mengasumsikan Anda ingin menggunakan nama properti / bidang yang akhirnya diakses dari proyeksi. (Jadi contoh Anda setara dengan Mo0gles ')
Chris Pfohl
1
menemukan jawaban saya. Saya perlu mendefinisikan entitas baru (MyViewEntity) yang berisi properti Column1 dan Column2 dan tipe kembaliannya adalah: IEnumerable <IGrouping <MyViewEntity, MyEntity >> dan kode pengelompokan snip adalah: MyEntityList.GroupBy (myEntity => MyViewEntity baru {Column1 = myEntity {Column1 = myEntity). Column1, Column2 = myEntity.Column2});
Amir Chatrbahr
469

Ok mengerti ini sebagai:

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();
Sreedhar
sumber
75
+1 - Terima kasih atas contoh lengkapnya. Cuplikan jawaban lainnya terlalu pendek dan tanpa konteks. Anda juga menunjukkan fungsi agregat (Jumlah dalam kasus ini). Sangat membantu. Saya menemukan penggunaan fungsi agregat (yaitu, MAX, MIN, SUM, dll.) Berdampingan dengan pengelompokan menjadi skenario umum.
barrypicker
Di sini: stackoverflow.com/questions/14189537/… , ditampilkan untuk tabel data ketika pengelompokan didasarkan pada satu kolom, yang namanya diketahui, tetapi bagaimana hal itu dapat dilakukan jika kolom berdasarkan pada pengelompokan yang harus dilakukan harus dihasilkan secara dinamis?
bg
Ini sangat membantu dalam memahami konsep pengelompokan dan penerapan agregasi di atasnya.
rajibdotnet
1
Contoh yang bagus ... hanya apa yang saya cari. Saya bahkan membutuhkan agregat jadi ini adalah jawaban yang sempurna walaupun saya mencari lambda saya mendapat cukup dari itu untuk menyelesaikan kebutuhan saya.
Kevbo
153

Untuk Kelompokkan Dengan Banyak Kolom, Coba ini ...

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

Cara yang sama Anda dapat menambahkan Column3, Column4 dll.

Milan
sumber
4
Itu sangat membantu dan harus mendapatkan lebih banyak upvotes! Resultberisi semua set data yang ditautkan ke semua kolom. Terima kasih banyak!
j00hi
1
note: Saya harus menggunakan .AsEnumerable () dan bukan ToList ()
GMan
1
Luar biasa, terima kasih untuk ini. Inilah contoh saya. Perhatikan bahwa GetFees mengembalikan IQueryable <Fee> RegistryAccountDA.GetFees (registryAccountId, fromDate, toDate) .GroupBy (x => new {x.AccountId, x.FeeName}, (kunci, grup) => baru {AccountId = key.AccountId , FeeName = key.FeeName, AppliedFee = group.Sum (x => x.AppliedFee) ?? 0M}). Daftar ();
Craig B
Apakah mungkin untuk mendapatkan kolom lain dari permintaan ini, yang tidak dikelompokkan? Jika ada array objek, saya ingin objek ini dikelompokkan berdasarkan dua kolom, tetapi dapatkan semua properti dari objek, bukan hanya dua kolom itu.
FrenkyB
30

Karena C # 7 Anda juga dapat menggunakan nilai tupel:

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

atau

.GroupBy(x => (x.Column1, x.Column2))
Nathan Tregillus
sumber
2
Yah saya pikir Anda kehilangan tambahan) pada akhirnya. Anda tidak akan menutup ()
StuiterSlurf
Saya telah menambahkannya.
Nathan Tregillus
2
.GroupBy (x => baru {x.Column1, x.Column2})
Zahidul Islam Jamy
19

C # 7.1 atau lebih besar menggunakan Tuplesdan Inferred tuple element names(saat ini hanya bekerja dengan linq to objectsdan itu tidak didukung ketika pohon ekspresi diperlukan misalnya someIQueryable.GroupBy(...). Masalah Github ):

// 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 atau lebih besar menggunakan 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) });
AlbertK
sumber
18

Anda juga dapat menggunakan Tuple <> untuk pengelompokan yang sangat diketik.

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),
}
Jay Bienvenu
sumber
4
Catatan: Membuat Tuple baru tidak didukung di Linq To Entities
bodoh
8

Meskipun pertanyaan ini menanyakan tentang properti grup per kelas, jika Anda ingin mengelompokkan beberapa kolom terhadap objek ADO (seperti DataTable), Anda harus menetapkan item "baru" ke variabel:

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);
Chris Smith
sumber
1
Saya tidak dapat melakukan permintaan Linq "grup c dengan {c.Field <String> (" Judul ") baru, c.Field <String> (" CIF ")}", dan Anda menghemat banyak waktu !! kueri terakhir adalah: "grup c dengan new {titulo = c.Field <String> (" Title "), cif = c.Field <String> (" CIF ")}"
netadictos
4
var Results= query.GroupBy(f => new { /* add members here */  });
Arindam
sumber
7
Tidak menambahkan apa pun ke jawaban sebelumnya.
Mike Fuchs
4
.GroupBy(x => x.Column1 + " " + x.Column2)
Kai Hartmann
sumber
Dikombinasikan dengan Linq.Enumerable.Aggregate()bahkan ini memungkinkan untuk mengelompokkan oleh sejumlah dinamis sifat: propertyValues.Aggregate((current, next) => current + " " + next).
Kai Hartmann
3
Ini adalah jawaban yang lebih baik daripada siapa pun yang memberi pujian. Mungkin bermasalah jika bisa ada contoh kombinasi di mana kolom1 ditambahkan ke kolom2 akan sama dengan situasi di mana kolom1 berbeda ("ab" "cde" akan cocok dengan "abc" "de"). Yang mengatakan, ini adalah solusi yang bagus jika Anda tidak dapat menggunakan tipe dinamis karena Anda pra-membangun lambdas setelah grup dengan ekspresi yang terpisah.
Brandon Barkley
3
"ab" "cde" seharusnya tidak cocok dengan "abc" "de", karenanya kosong di antaranya.
Kai Hartmann
1
bagaimana dengan "abc de" "" dan "abc" "de"?
AlbertK
@AlbertK itu tidak akan berhasil, kurasa.
Kai Hartmann
3

.GroupBy(x => (x.MaterialID, x.ProductID))

Ayo Enkindle
sumber
3
Pertimbangkan untuk menambahkan penjelasan tentang bagaimana kode ini menyelesaikan masalah.
Rosário Pereira Fernandes
2

grup x dengan {x.Col, x.Col} baru

John
sumber
1

Satu hal yang perlu diperhatikan adalah Anda perlu mengirimkan objek untuk ekspresi Lambda dan tidak dapat menggunakan instance untuk kelas.

Contoh:

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

    public string Prop2 { get; set; }
}

Ini akan dikompilasi tetapi akan menghasilkan satu kunci per siklus .

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

Jika Anda tidak ingin memberi nama properti kunci dan kemudian mengambilnya, Anda bisa melakukannya seperti ini. Ini akan GroupBydengan benar dan memberi Anda properti kunci.

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;
}
Ogglas
sumber
0

Untuk VB dan anonim / lambda :

query.GroupBy(Function(x) New With {Key x.Field1, Key x.Field2, Key x.FieldN })
Dani
sumber