J'ai fait une recherche sur le Web mais je n'arrive toujours pas à trouver une réponse simple. Quelqu'un peut-il s'il vous plaît expliquer (en anglais simple) ce qu'est un GroupJoin
? En quoi est-il différent d'un Join
interne normal? Est-il couramment utilisé? Est-ce seulement pour la syntaxe de la méthode? Qu'en est-il de la syntaxe de requête? Un exemple de code c # serait Nice.
Supposons que vous ayez deux listes:
Id Value
1 A
2 B
3 C
Id ChildValue
1 a1
1 a2
1 a3
2 b1
2 b2
Lorsque vous Join
les deux listes du champ Id
, le résultat sera:
Value ChildValue
A a1
A a2
A a3
B b1
B b2
Lorsque vous GroupJoin
les deux listes du champ Id
, le résultat sera:
Value ChildValues
A [a1, a2, a3]
B [b1, b2]
C []
Donc, Join
produit un résultat plat (tabulaire) des valeurs parent et enfant.GroupJoin
produit une liste d'entrées dans la première liste, chacune avec un groupe d'entrées jointes dans la deuxième liste.
C'est pourquoi Join
est l'équivalent de INNER JOIN
en SQL: il n'y a pas d'entrées pour C
. Alors que GroupJoin
équivaut à OUTER JOIN
: C
figure dans le jeu de résultats, mais avec une liste vide d'entrées associées (dans un jeu de résultats SQL, il y aurait une ligne C - null
).
Alors laissez les deux listes être IEnumerable<Parent>
et IEnumerable<Child>
respectivement. (Dans le cas de Linq to Entities: IQueryable<T>
).
Join
la syntaxe serait
from p in Parent
join c in Child on p.Id equals c.Id
select new { p.Value, c.ChildValue }
renvoyer un IEnumerable<X>
où X est un type anonyme avec deux propriétés, Value
et ChildValue
. Cette syntaxe de requête utilise la méthode Join
sous le capot.
GroupJoin
la syntaxe serait
from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }
renvoyer un IEnumerable<Y>
où Y est un type anonyme composé d'une propriété de type Parent
et d'une propriété de type IEnumerable<Child>
. Cette syntaxe de requête utilise la méthode GroupJoin
sous le capot.
Nous pourrions simplement faire select g
dans la dernière requête, ce qui sélectionnerait un IEnumerable<IEnumerable<Child>>
, par exemple une liste de listes. Dans de nombreux cas, la sélection avec le parent inclus est plus utile.
Comme dit, la déclaration ...
from p in Parent
join c in Child on p.Id equals c.Id into g
select new { Parent = p, Children = g }
... produit une liste de parents avec des groupes d'enfants. Cela peut être transformé en une liste plate de paires parent-enfant par deux petits ajouts:
from p in parents
join c in children on p.Id equals c.Id into g // <= into
from c in g.DefaultIfEmpty() // <= flattens the groups
select new { Parent = p.Value, Child = c?.ChildValue }
Le résultat est similaire à
Value Child
A a1
A a2
A a3
B b1
B b2
C (null)
Notez que la variable de plage c
est réutilisée dans l'instruction ci-dessus. Ainsi, toute instruction join
peut simplement être convertie en un outer join
en ajoutant l’équivalent de into g from c in g.DefaultIfEmpty()
à une instruction join
existante.
C'est là que la syntaxe de requête (ou complète) brille. La syntaxe Method (ou fluent) montre ce qui se passe réellement, mais il est difficile d'écrire:
parents.GroupJoin(children, p => p.Id, c => c.Id, (p, c) => new { p, c })
.SelectMany(x => x.c.DefaultIfEmpty(), (x,c) => new { x.p.Value, c?.ChildValue } )
Ainsi, un outer join
plat de LINQ est un GroupJoin
, aplati par SelectMany
.
Supposons que la liste des parents est un peu plus longue. Certaines interfaces utilisateur génèrent une liste des parents sélectionnés sous forme de valeurs Id
dans un ordre fixe. Utilisons:
var ids = new[] { 3,7,2,4 };
Maintenant, les parents sélectionnés doivent être filtrés de la liste des parents dans cet ordre exact.
Si nous faisons ...
var result = parents.Where(p => ids.Contains(p.Id));
... l'ordre de parents
déterminera le résultat. Si les parents sont commandés par Id
, le résultat sera les parents 2, 3, 4, 7. Pas bien. Cependant, nous pouvons également utiliser join
pour filtrer la liste. Et en utilisant ids
comme première liste, l'ordre sera préservé:
from id in ids
join p in parents on id equals p.Id
select p
Le résultat est les parents 3, 7, 2, 4.
Selon eduLINQ :
Le meilleur moyen de comprendre ce que fait GroupJoin est de penser à Join. Là, l’idée générale était que nous avons examiné la séquence de saisie "externe", trouvé tous les éléments correspondants de la séquence "interne" (sur la base d’une projection de clé sur chaque séquence), puis produit des paires d’éléments correspondants. GroupJoin est similaire, sauf qu'au lieu de générer des paires d'éléments, il génère un seul résultat pour chaque élément "externe" en fonction de cet élément et de la séquence d'éléments "internes" correspondants .
La seule différence est dans l'instruction return:
Rejoindre :
var lookup = inner.ToLookup(innerKeySelector, comparer);
foreach (var outerElement in outer)
{
var key = outerKeySelector(outerElement);
foreach (var innerElement in lookup[key])
{
yield return resultSelector(outerElement, innerElement);
}
}
GroupJoin :
var lookup = inner.ToLookup(innerKeySelector, comparer);
foreach (var outerElement in outer)
{
var key = outerKeySelector(outerElement);
yield return resultSelector(outerElement, lookup[key]);
}
Lire la suite ici: