Question Grouper par dans LINQ


Supposons que nous ayons une classe comme

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

Maintenant j'ai une liste de cette classe: List<Person> persons;

Maintenant, cette liste peut avoir plusieurs instances avec les mêmes PersonID, par exemple:

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

Existe-t-il un moyen de regrouper par personID et obtenir la liste de toutes les voitures qu'il a?

Par exemple, le résultat attendu serait

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

Donc, après le regroupement, j'obtiendrais:

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

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

De ce que j'ai fait jusqu'ici:

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

Quelqu'un pourrait-il me diriger dans la bonne direction?


806
2017-09-06 19:44


origine


Réponses:


Absolument - vous voulez essentiellement:

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

Ou en tant qu'expression sans requête:

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

Fondamentalement, le contenu du groupe (lorsque vue en tant que IEnumerable<T>) est une séquence de toutes les valeurs présentes dans la projection (p.car dans ce cas) présent pour la clé donnée.

Pour en savoir plus sur comment GroupBy fonctionne, voir mon Edulinq post sur le sujet.

(J'ai renommé PersonID à PersonId dans le dessus, à suivre Conventions de dénomination .NET.)

Alternativement, vous pouvez utiliser un Lookup:

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

Vous pouvez ensuite obtenir les voitures pour chaque personne très facilement:

// 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



Vous pouvez également essayer ceci.

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



essayer

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

ou

pour vérifier si une personne se répète dans votre liste essayer

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

12
2017-08-15 22:24



J'ai créé un exemple de code de travail avec la syntaxe de requête et la syntaxe de méthode. J'espère que ça aide les autres :)

Vous pouvez également exécuter le code sur .Net Fiddle ici:

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

Voici le résultat:

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


6
2017-11-30 19:12



Essaye ça :

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

Mais en ce qui concerne les performances, la pratique suivante est meilleure et plus optimisée dans l'utilisation de la mémoire (lorsque notre tableau contient beaucoup plus d'éléments comme des millions):

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