Frage Gruppieren nach in LINQ


Nehmen wir an, wir hätten eine Klasse wie

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

Jetzt habe ich eine Liste dieser Klasse: List<Person> persons;

Jetzt kann diese Liste mehrere Instanzen mit denselben PersonIDs haben, zum Beispiel:

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

Gibt es einen Weg, nach dem ich gruppieren kann? personID und die Liste aller Autos bekommen, die er hat?

Zum Beispiel wäre das erwartete Ergebnis

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

Also nach der Gruppierung würde ich bekommen:

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

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

Von dem was ich bisher gemacht habe:

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

Könnte mir bitte jemand in die richtige Richtung zeigen?


806
2017-09-06 19:44


Ursprung


Antworten:


Absolut - Sie wollen grundsätzlich:

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

Oder als Nicht-Abfrage-Ausdruck:

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

Grundsätzlich der Inhalt der Gruppe (wenn als IEnumerable<T>) ist eine Folge von beliebigen Werten in der Projektion (p.car in diesem Fall) für den gegebenen Schlüssel vorhanden.

Für mehr darüber wie GroupBy funktioniert, siehe meine Edulinq post zu dem Thema.

(Ich habe umbenannt PersonID zu PersonId im obigen, um zu folgen .NET-Namenskonventionen.)

Alternativ könnten Sie eine verwenden Lookup:

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

Sie können dann die Autos für jede Person sehr leicht bekommen:

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

1365
2017-09-06 19:46



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()/**/}

44
2018-02-05 17:25



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

28
2017-08-18 10:26



Sie können dies auch versuchen.

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

23
2017-12-02 06:15



Versuchen

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

oder

Überprüfen Sie, ob sich eine Person in Ihrer Liste wiederholt

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

12
2017-08-15 22:24



Ich habe ein funktionierendes Codebeispiel mit Abfragesyntax und Methodensyntax erstellt. Ich hoffe es hilft den anderen :)

Sie können den Code auch hier auf .Net Fiddle ausführen:

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);
            }
        }
    }
}

Hier ist das Ergebnis:

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


6
2017-11-30 19:12



Versuche dies :

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

Im Hinblick auf die Leistung ist die folgende Praxis jedoch besser und optimierter in der Speichernutzung (wenn unser Array viel mehr Elemente wie Millionen enthält):

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];

2
2018-05-26 05:51