Utilisation de Linq pour regrouper une liste d'objets dans une nouvelle liste groupée d'objets

149

Je ne sais pas si cela est possible à Linq mais voilà ...

J'ai un objet:

public class User
{
  public int UserID { get; set; }
  public string UserName { get; set; }
  public int GroupID { get; set; }
}

Je renvoie une liste qui peut ressembler à ceci:

List<User> userList = new List<User>();
userList.Add( new User { UserID = 1, UserName = "UserOne", GroupID = 1 } );
userList.Add( new User { UserID = 2, UserName = "UserTwo", GroupID = 1 } );
userList.Add( new User { UserID = 3, UserName = "UserThree", GroupID = 2 } );
userList.Add( new User { UserID = 4, UserName = "UserFour", GroupID = 1 } );
userList.Add( new User { UserID = 5, UserName = "UserFive", GroupID = 3 } );
userList.Add( new User { UserID = 6, UserName = "UserSix", GroupID = 3 } );

Je veux pouvoir exécuter une requête Linq sur la liste ci-dessus qui regroupe tous les utilisateurs par GroupID. Ainsi, la sortie sera une liste de listes d'utilisateurs contenant l'utilisateur (si cela a du sens?). Quelque chose comme:

GroupedUserList
    UserList
        UserID = 1, UserName = "UserOne", GroupID = 1
        UserID = 2, UserName = "UserTwo", GroupID = 1
        UserID = 4, UserName = "UserFour", GroupID = 1
    UserList
        UserID = 3, UserName = "UserThree", GroupID = 2
    UserList
        UserID = 5, UserName = "UserFive", GroupID = 3
        UserID = 6, UserName = "UserSix", GroupID = 3

J'ai essayé d'utiliser la clause groupby linq mais cela semble renvoyer une liste de clés et elle n'est pas regroupée correctement:

var groupedCustomerList = userList.GroupBy( u => u.GroupID ).ToList();
lancscoder
la source

Réponses:

309
var groupedCustomerList = userList
    .GroupBy(u => u.GroupID)
    .Select(grp => grp.ToList())
    .ToList();
Lee
la source
1
très sympa, exactement ce dont j'avais besoin. Merci. faire cela de manière impérative est nul.
user1841243
1
Ça marche bien? parce que je suis habitué de cette façon n'a pas fonctionné. objModel.tblDonars.GroupBy (t => new {t.CreatedOn.Year, t.CreatedOn.Month, t.CreatedOn.Day}). Sélectionnez (g => new {tblDonar = g.ToList ()}). ToList ( ); cela ne fonctionne pas ... pouvez-vous aider ....
Rajamohan Anguchamy
avec votre solution, cela fonctionne bien, mais j'ai une question supposons que dans le groupe jusqu'à 8 valeur et je veux juste avoir besoin dans chaque groupe avec seulement 6 prises, alors comment faire cela s'il vous plaît laissez-moi savoir.
coderwill
existe-t-il un moyen de lister les noms d'utilisateur et les ID utilisateur séparément après le regroupement par GroupID? comme - {"1", [1, 2, 4], ["UserOne", "UserTwo", "UserFour"]} pour groupID 1.
Ajay Aradhya
1
Actuellement, le code ne fonctionne pas et provoque une erreur si vous essayez de le convertir dans le type de liste précédent. Parce que retourner une liste dans select crée une liste à l'intérieur d'une liste qui n'est pas la sortie souhaitée ici. Pour ceux qui ont des problèmes, je peux suggérer: var groupedCustomerList = userList.GroupBy (u => u.GroupID) .Select (grp => grp.First ()). ToList ();
aliassce le
36

Votre déclaration de groupe sera regroupée par ID de groupe. Par exemple, si vous écrivez ensuite:

foreach (var group in groupedCustomerList)
{
    Console.WriteLine("Group {0}", group.Key);
    foreach (var user in group)
    {
        Console.WriteLine("  {0}", user.UserName);
    }
}

cela devrait fonctionner correctement. Chaque groupe a une clé, mais contient également un IGrouping<TKey, TElement>qui est une collection qui vous permet d'itérer sur les membres du groupe. Comme Lee le mentionne, vous pouvez convertir chaque groupe en une liste si vous le souhaitez vraiment, mais si vous allez simplement les parcourir selon le code ci-dessus, il n'y a aucun avantage réel à le faire.

Jon Skeet
la source
4

Pour le type

public class KeyValue
{
    public string KeyCol { get; set; }
    public string ValueCol { get; set; }
}

collection

var wordList = new Model.DTO.KeyValue[] {
    new Model.DTO.KeyValue {KeyCol="key1", ValueCol="value1" },
    new Model.DTO.KeyValue {KeyCol="key2", ValueCol="value1" },
    new Model.DTO.KeyValue {KeyCol="key3", ValueCol="value2" },
    new Model.DTO.KeyValue {KeyCol="key4", ValueCol="value2" },
    new Model.DTO.KeyValue {KeyCol="key5", ValueCol="value3" },
    new Model.DTO.KeyValue {KeyCol="key6", ValueCol="value4" }
};

notre requête linq ressemble à ci-dessous

var query =from m in wordList group m.KeyCol by m.ValueCol into g
select new { Name = g.Key, KeyCols = g.ToList() };

ou pour tableau au lieu de liste comme ci-dessous

var query =from m in wordList group m.KeyCol by m.ValueCol into g
select new { Name = g.Key, KeyCols = g.ToList().ToArray<string>() };
sangram
la source
-1

Encore un ancien, mais la réponse de Lee ne m'a pas donné le groupe. Clé en conséquence. Par conséquent, j'utilise l'instruction suivante pour grouper une liste et renvoyer une liste groupée:

public IOrderedEnumerable<IGrouping<string, User>> groupedCustomerList;

groupedCustomerList =
        from User in userList
        group User by User.GroupID into newGroup
        orderby newGroup.Key
        select newGroup;

Chaque groupe a maintenant une clé, mais contient également un IGrouping qui est une collection qui vous permet d'itérer sur les membres du groupe.

datapool
la source
Cela répond à votre question, pas à celle d'OP. Ils veulent seulement une liste de listes. Votre code ne fournit pas cela.
Gert Arnold
J'ai publié cette solution car la réponse acceptée ci-dessus de @Lee ne fonctionne pas lors de l'itération car elle ne fournit pas l' group.keyélément lors de l'itération dans la liste groupée comme indiqué dans la réponse de @JohnSkeet. Ma réponse fournie ne fournir l'élément group.key lorsque itérer. Cela pourrait donc aider d'autres à tomber dans le piège que les réponses fournies ne fonctionnent pas comme indiqué ci-dessus. C'est donc juste une autre façon d'obtenir la liste avec l'avantage d'obtenir l'élément group.key similaire à la réponse acceptée. Ne comprenez pas votre réclamation @GertArnold. (.net core 3.0)
datapool
Je pense que toute la question est basée sur un manque de compréhension de ce que IGroupingc'est. C'est pourquoi OP supprime son propre code correct en bas, qui est essentiellement identique à votre code. C'est pourquoi ils acceptent une réponse qui fournit exactement ce qu'ils demandent. Ce dont ils ont besoin, c'est d'une explication, que JS a suffisamment fournie.
Gert Arnold