Dikelompokkan oleh dalam LINQ


1063

Misalkan jika kita memiliki kelas seperti:

class Person { 
    internal int PersonID; 
    internal string car; 
}

Sekarang saya memiliki daftar kelas ini: List<Person> persons;

Sekarang daftar ini dapat memiliki beberapa instance dengan yang sama PersonID, misalnya:

persons[0] = new Person { PersonID = 1, car = "Ferrari" }; 
persons[1] = new Person { PersonID = 1, car = "BMW"     }; 
persons[2] = new Person { PersonID = 2, car = "Audi"    }; 

Apakah ada cara saya dapat mengelompokkan berdasarkan PersonIDdan mendapatkan daftar semua mobil yang dimilikinya?

Misalnya, hasil yang diharapkan adalah

class Result { 
   int PersonID;
   List<string> cars; 
}

Jadi setelah pengelompokan, saya akan mendapatkan:

results[0].PersonID = 1; 
List<string> cars = results[0].cars; 

result[1].PersonID = 2; 
List<string> cars = result[1].cars;

Dari apa yang telah saya lakukan sejauh ini:

var results = from p in persons
              group p by p.PersonID into g
              select new { PersonID = g.Key, // this is where I am not sure what to do

Bisakah seseorang tolong tunjukkan saya ke arah yang benar?


1
Ada contoh lain termasuk Countdan di Sumsini stackoverflow.com/questions/3414080/…
Pengembang

@ Martin Källman: Saya setuju dengan Chris Walsh. Kemungkinan besar sebuah aplikasi yang memiliki Kelas (Tabel) "Orang (s)" OP sudah memiliki "(normal)" "Kelas" (Orang) "yang memiliki usu. Properti / Kolom (yaitu nama, jenis kelamin, DOB). "Kelas (Tabel)" OP (Tabel) akan menjadi Kelas Anak (Tabel) dari Kelas (Tabel) "Tabel" (")" (normal) "(yaitu)" (normal) "Kelas (Tabel) ) vs. Kelas "Tabel" (Pesanan) OP kemungkinan tidak menggunakan nama sebenarnya yang akan ia gunakan jika berada dalam Lingkup yang sama dengan Kelas (Tabel) "(normal)" "dan / atau mungkin disederhanakan untuk posting ini.
Tom

Jawaban:


1735

Tentu - Anda pada dasarnya ingin:

var results = from p in persons
              group p.car by p.PersonId into g
              select new { PersonId = g.Key, Cars = g.ToList() };

Atau sebagai ekspresi non-permintaan:

var results = persons.GroupBy(
    p => p.PersonId, 
    p => p.car,
    (key, g) => new { PersonId = key, Cars = g.ToList() });

Pada dasarnya isi grup (bila dilihat sebagai IEnumerable<T>) adalah urutan nilai apa pun yang ada dalam proyeksi ( p.cardalam hal ini) hadir untuk kunci yang diberikan.

Untuk lebih lanjut tentang cara GroupBykerjanya, lihat posting Edulinq saya pada topik .

(Saya telah berganti nama PersonIDmenjadi PersonIddi atas, untuk mengikuti konvensi penamaan .NET )

Atau, Anda dapat menggunakan Lookup:

var carsByPersonId = persons.ToLookup(p => p.PersonId, p => p.car);

Anda kemudian dapat memperoleh mobil untuk setiap orang dengan sangat mudah:

// This will be an empty sequence for any personId not in the lookup
var carsForPerson = carsByPersonId[personId];

11
@jon Skeet bagaimana jika saya ingin menambahkan properti lain seperti nama
user123456

22
@Mohammad: Kemudian Anda memasukkannya ke dalam jenis anonim.
Jon Skeet

7
@ user123456 inilah penjelasan yang baik tentang grup oleh, juga termasuk contoh pengelompokan dengan kunci komposit: Cara: Hasil Kueri Grup (Panduan Pemrograman C #)
Mathieu Diepman

14
@Mohammad Anda dapat melakukan sesuatu seperti .GroupBy(p => new {p.Id, p.Name}, p => p, (key, g) => new { PersonId = key.Id, PersonName = key.Name, PersonCount = g.Count()})dan Anda akan mendapatkan semua orang yang muncul dengan Id, Nama, dan sejumlah kejadian untuk setiap orang.
Chris

11
@ame: Saya sengaja mengikuti konvensi penamaan .NET, memperbaiki nama OP, pada dasarnya. Akan memperjelas jawabannya.
Jon Skeet

52
var results = from p in persons
              group p by p.PersonID into g
              select new { PersonID = g.Key,
                           /**/car = g.Select(g=>g.car).FirstOrDefault()/**/}

37
var results = from p in persons
              group p by p.PersonID into g
              select new { PersonID = g.Key, Cars = g.Select(m => m.car) };

32

Anda juga dapat Coba ini:

var results= persons.GroupBy(n => new { n.PersonId, n.car})
                .Select(g => new {
                               g.Key.PersonId,
                               g.Key.car)}).ToList();

Itu salah mengembalikan daftar yang sama tidak dikelompokkan oleh
Vikas Gupta

Itu bekerja, saya pikir ada sesuatu yang hilang itu sebabnya itu tidak berfungsi dalam kode Anda.
shuvo sarker

29

mencoba

persons.GroupBy(x => x.PersonId).Select(x => x)

atau

untuk memeriksa apakah ada orang yang mengulang dalam daftar Anda, cobalah

persons.GroupBy(x => x.PersonId).Where(x => x.Count() > 1).Any(x => x)

13

Saya telah membuat contoh kode kerja dengan Query Syntax dan Method Syntax. Saya harap ini membantu yang lain :)

Anda juga dapat menjalankan kode pada .Net Fiddle di sini:

using System;
using System.Linq;
using System.Collections.Generic;

class Person
{ 
    public int PersonId; 
    public string car  ; 
}

class Result
{ 
   public int PersonId;
   public List<string> Cars; 
}

public class Program
{
    public static void Main()
    {
        List<Person> persons = new List<Person>()
        {
            new Person { PersonId = 1, car = "Ferrari" },
            new Person { PersonId = 1, car = "BMW" },
            new Person { PersonId = 2, car = "Audi"}
        };

        //With Query Syntax

        List<Result> results1 = (
            from p in persons
            group p by p.PersonId into g
            select new Result()
                {
                    PersonId = g.Key, 
                    Cars = g.Select(c => c.car).ToList()
                }
            ).ToList();

        foreach (Result item in results1)
        {
            Console.WriteLine(item.PersonId);
            foreach(string car in item.Cars)
            {
                Console.WriteLine(car);
            }
        }

        Console.WriteLine("-----------");

        //Method Syntax

        List<Result> results2 = persons
            .GroupBy(p => p.PersonId, 
                     (k, c) => new Result()
                             {
                                 PersonId = k,
                                 Cars = c.Select(cs => cs.car).ToList()
                             }
                    ).ToList();

        foreach (Result item in results2)
        {
            Console.WriteLine(item.PersonId);
            foreach(string car in item.Cars)
            {
                Console.WriteLine(car);
            }
        }
    }
}

Inilah hasilnya:

1
Ferrari
BMW
2
Audi
-----------
1
Ferrari
BMW
2
Audi


Tolong, jelaskan apa yang kode Anda lakukan demi. Ini hanya jawaban kode yang hampir merupakan jawaban yang salah.
V.7

2

Coba ini :

var results= persons.GroupBy(n => n.PersonId)
            .Select(g => new {
                           PersonId=g.Key,
                           Cars=g.Select(p=>p.car).ToList())}).ToList();

Namun berdasarkan kinerja, praktik berikut ini lebih baik dan lebih dioptimalkan dalam penggunaan memori (ketika array kami berisi lebih banyak item seperti jutaan):

var carDic=new Dictionary<int,List<string>>();
for(int i=0;i<persons.length;i++)
{
   var person=persons[i];
   if(carDic.ContainsKey(person.PersonId))
   {
        carDic[person.PersonId].Add(person.car);
   }
   else
   {
        carDic[person.PersonId]=new List<string>(){person.car};
   }
}
//returns the list of cars for PersonId 1
var carList=carDic[1];

4
g.Key.PersonId? g.SelectMany?? Anda jelas tidak mencoba ini.
Gert Arnold

Anda menulis saya mengedit beberapa kode kode di dalamnya dan tidak mengujinya. Poin utama saya adalah bagian kedua. Tapi bagaimanapun, terima kasih atas pertimbangan Anda. Sudah terlambat untuk mengedit kode itu ketika saya menyadari itu salah. jadi g.Key menggantikan g.Key.PersonId, dan Pilih daripada SelectMany! jadi berantakan maaf :)))
akazemis

2
@akazemis: Saya sebenarnya mencoba membuat (menggunakan istilah yang setara dengan domain OP) SortedDictionary <PersonIdInt, SortedDictionary <CarNameString, CarInfoClass>>. Yang paling dekat saya bisa menggunakan LINQ adalah IEnumerable <IGrouping <PersonIdInt, Dictionary <CarNameString, PersonIdCarNameXrefClass>>>. Saya berakhir menggunakan formetode loop Anda yang, btw, 2x lebih cepat. Juga, saya akan menggunakan: a) foreachvs. fordan b) TryGetValuevs. ContainsKey(keduanya untuk prinsip KERING - dalam kode & runtime).
Tom

1

Cara alternatif untuk melakukan ini dapat memilih PersonIdgrup berbeda dan bergabung dengan persons:

var result = 
    from id in persons.Select(x => x.PersonId).Distinct()
    join p2 in persons on id equals p2.PersonId into gr // apply group join here
    select new 
    {
        PersonId = id,
        Cars = gr.Select(x => x.Car).ToList(),
    };

Atau sama dengan sintaks API yang lancar:

var result = persons.Select(x => x.PersonId).Distinct()
    .GroupJoin(persons, id => id, p => p.PersonId, (id, gr) => new
    {
        PersonId = id,
        Cars = gr.Select(x => x.Car).ToList(),
    });

GroupJoin menghasilkan daftar entri dalam daftar pertama (daftar PersonIddalam kasus kami), masing-masing dengan sekelompok entri yang tergabung dalam daftar kedua (daftar persons).


1

Contoh berikut menggunakan metode GroupBy untuk mengembalikan objek yang dikelompokkan berdasarkan PersonID.

var results = persons.GroupBy(x => x.PersonID)
              .Select(x => (PersonID: x.Key, Cars: x.Select(p => p.car).ToList())
              ).ToList();

Atau

 var results = persons.GroupBy(
               person => person.PersonID,
               (key, groupPerson) => (PersonID: key, Cars: groupPerson.Select(x => x.car).ToList()));

Atau

 var results = from person in persons
               group person by person.PersonID into groupPerson
               select (PersonID: groupPerson.Key, Cars: groupPerson.Select(x => x.car).ToList());

Atau Anda dapat menggunakan ToLookup, Pada dasarnya ToLookupmenggunakan. EqualityComparer<TKey>Kesalahan untuk membandingkan kunci dan melakukan apa yang harus Anda lakukan secara manual saat menggunakan grup oleh dan ke kamus. Saya pikir itu dikecam di memori

 ILookup<int, string> results = persons.ToLookup(
            person => person.PersonID,
            person => person.car);

0

Pertama, setel bidang kunci Anda. Kemudian sertakan bidang Anda yang lain:

var results = 
    persons
    .GroupBy(n => n.PersonId)
    .Select(r => new Result {PersonID = r.Key, Cars = r.ToList() })
    .ToList()

Jawaban / kode yang tidak dikomentari bukanlah cara kerja StackOverflow ...
V.7
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.