Pregunta Agrupar por en LINQ


Supongamos que tenemos una clase como

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

Ahora tengo una lista de esta clase: List<Person> persons;

Ahora esta lista puede tener varias instancias con los mismos PersonIDs, por ejemplo:

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

¿Hay alguna manera de agrupar personID y obtener la lista de todos los autos que tiene?

Por ejemplo, el resultado esperado sería

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

Entonces, después de agruparme, obtendría:

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

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

Por lo que he hecho hasta ahora:

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

¿Podría alguien señalarme en la dirección correcta?


806
2017-09-06 19:44


origen


Respuestas:


Absolutamente, básicamente quieres:

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

O como una expresión sin consulta:

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

Básicamente, los contenidos del grupo (cuando se ven como IEnumerable<T>) es una secuencia de cualquier valor que haya en la proyección (p.car en este caso) presente para la clave dada.

Para más sobre cómo GroupBy funciona, mira mi Publicación de Edulinq sobre el tema.

(He cambiado el nombre PersonID a PersonId en lo de arriba, para seguir Convenciones de nomenclatura de .NET.)

Alternativamente, podrías usar un Lookup:

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

A continuación, puede obtener los automóviles para cada persona con mucha facilidad:

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



También puedes probar esto.

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



tratar

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

o

para verificar si alguna persona está repitiendo en su lista, intente

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

12
2017-08-15 22:24



Creé un ejemplo de código de trabajo con Query Syntax y Method Syntax. Espero que ayude a los demás :)

También puede ejecutar el código en .Net Fiddle aquí:

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

Aquí está el resultado:

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


6
2017-11-30 19:12



Prueba esto :

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

Pero en lo que respecta al rendimiento, la siguiente práctica es mejor y más optimizada en el uso de la memoria (cuando nuestra matriz contiene muchos más elementos, como millones):

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