web-dev-qa-db-fra.com

Grouper dans LINQ

Supposons que si nous avons 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 identifiants, 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"    }; 

Est-il possible de grouper par personID et d’obtenir la liste de toutes ses voitures?

Par exemple, le résultat attendu serait

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

Donc, après avoir groupé, j'obtiendrais:

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

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

D'après ce que j'ai fait jusqu'à présent:

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

Est-ce que quelqu'un pourrait m'indiquer dans la bonne direction?

971
test123

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 (lorsqu'il est visualisé sous la forme d'un 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 plus d'informations sur le fonctionnement de GroupBy, voir mon article d'Edulinq sur le sujet .

(J'ai renommé PersonID en PersonId ci-dessus, pour suivre conventions de dénomination .NET .)

Alternativement, vous pouvez utiliser un Lookup:

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

Vous pouvez alors 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];
1621
Jon Skeet
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()/**/}
48
Tallat
var results = from p in persons
              group p by p.PersonID into g
              select new { PersonID = g.Key, Cars = g.Select(m => m.car) };
35
Yogendra Paudyal

Vous pouvez aussi essayer ceci.

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

essayer

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

ou

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

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

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 
 
13
Recev Yildiz

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 termes de performances, la pratique suivante est meilleure et plus optimisée en termes d’utilisation de la mémoire (lorsque notre tableau contient beaucoup plus d’articles, par exemple, 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
akazemis

Une autre façon de faire cela pourrait être de sélectionner distincte PersonId et de joindre le groupe avec 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(),
    };

Ou la même chose avec la syntaxe API fluide:

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 produit une liste d'entrées dans la première liste (liste de PersonId dans notre cas), chacune avec un groupe d'entrées jointes dans la deuxième liste (liste de persons) .

1
Dmitry Stepanov