Le billet d’aujourd’hui est à propos d’une méthode du framework .NET. SelectMany, vous connaissez? Pour ma part, je le connaissais seulement de par son nom. Surtout par ce qu’elle est très près de Select dans l’IntelliSense.
Ironiquement, c’est souvent de cette façon-là que je découvre de nouvelles fonctionnalités dans le framework .NET. Dans mon scénario, j’avais besoin de retrouver tous les départements liés à une collection d’utilisateurs.
Pour donner une idée, la structure de données et son initialisation prennent approximativement cette forme:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class User | |
{ | |
public Guid Id { get; set; } | |
public string Name { get; set; } | |
public List<Department> Departments { get; set; } | |
} | |
public class Department | |
{ | |
public Guid Id { get; set; } | |
public string Name { get; set; } | |
} | |
var users = new List<User>(); | |
users.Add(new User | |
{ | |
Departments = new List<Department> | |
{ | |
new Department | |
{ | |
Name = "Dept 1" | |
}, | |
new Department | |
{ | |
Name = "Dept 2" | |
}, | |
}, | |
Name = "French Coding", | |
}); | |
users.Add(new User | |
{ | |
Departments = new List<Department> | |
{ | |
new Department | |
{ | |
Name = "Dept 2" | |
}, | |
new Department | |
{ | |
Name = "Dept 3" | |
}, | |
new Department | |
{ | |
Name = "Dept 4" | |
}, | |
}, | |
Name = "Homme Poux", | |
}); | |
users.Add(new User | |
{ | |
Departments = new List<Department> | |
{ | |
new Department | |
{ | |
Name = "Dept 1" | |
}, | |
new Department | |
{ | |
Name = "Dept 4" | |
}, | |
}, | |
Name = "Pascal Paradis", | |
}); |
Mon premier réflexe, afin de récupérer la liste de tous les départements, aurait été de faire une boucle comme celle-ci.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var departments = new List<Department>(); | |
foreach(var user in users) | |
{ | |
departments.AddRange(user.Departments); | |
} |
Comme dirait l’autre: « Ça marche!« . Le seul problème que j’ai toujours eu avec cette approche c’est que c’est laid. C’est fonctionnel, mais c’est laid pareil.
Ceci étant dit, c’est à partir de cet instant que j’ai tenté de voir s’il n’y avait pas mieux. C’est en faisant mes recherches et, notamment, en utilisant les mots clés « C# LINQ List of List » que mon attention a été attirée vers SelectMany.
Je suis trop bon à Google, n’est-ce pas?
Comme la documentation MSDN le spécifie, SelectMany permet d’aplatir un résultat qui contient une liste d’une liste en une seule liste. Aussi simple que ça. Cependant, elle permet de faire un peu plus que cela. Par exemple, il est possible d’inclure des données de la collection parente dans la liste qui sera aplatie.
En reprenant l’initialisation mentionnée précédemment, considérez le code suivant.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var results = users.SelectMany(p => p.Departments, (user, departement) => new Tuple<string, string>(user.Name, departement.Name)); | |
foreach(var userAndDepartment in results) | |
{ | |
Console.WriteLine(string.Format("User: {0} – Department: {1}", userAndDepartment.Item1, userAndDepartment.Item2)); | |
} | |
Console.ReadKey(); |
Va permettre de retourner de l’information comme ceci.
Voilà, c’est tout!
Ta première boucle fait pas la même chose (te faudrait un Dictionnary ou un Tupple comme t’as fait dans ta deuxième implémentation. De plus, je pense que la première approche est beaucoup plus lisible, mais bon, c’est juste moi :P
oh! Tellement vrai pour la première boucle! Est-ce que ça parait que j’ai écris le billet sur deux journées différentes?
Attends, je viens de me relire! C’est pas la même chose mais c’est « prévu » ainsi. Dans https://gist.github.com/pparadis/0844f30f5ef39726d7f1#file-select-many-with-parent-cs, j’ai voulu démontrer c’était quoi la signature « compliqué » de SelectMany.