94. SelectMany extension method

נניח שיש לנו אוסף של משפחות ואנחנו רוצים למצוא את כל הילדים בה.

אז יש לנו משפחה שמיוצגת ע”י טיפוס היורש מIEnumerable, ואוסף של משפחות הוא IEnumerable של כאלה.

נניח למשל שמשפחה היא פשוט IEnumerable:

1
2
3
4
5
IEnumerable<IEnumerable<Person>> families;
IEnumerable<IEnumerable<Person>> children =
families.Select
(family => family.Where(person => person.Age <= 12));

אלא שchildren הוא IEnumerable<IEnumerable<Person>>, ולא IEnumerable<Person> כפי שהיינו מצפים.

בכדי לפתור את זה, קיים הExtension Method ששמו SelectMany המאפשר לנו לשטח Enumerable<IEnumerable<T>> לIEnumerable<T>.

שימוש:

1
2
3
4
5
6
IEnumerable<IEnumerable<Person>> childrenEnumerable =
families.Select
(family => family.Where(person => person.Age <= 12));
IEnumerable<Person> children =
childrenEnumerable.SelectMany(person => person);

או פשוט כך:

1
2
3
IEnumerable<Person> children =
families.SelectMany
(family => family.Where(person => person.Age <= 12));

המימוש הוא משהו כזה:

1
2
3
4
5
6
7
8
9
10
11
public static IEnumerable<TResult> SelectMany<TSource, TResult>
(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> selector)
{
foreach (TSource current in source)
{
foreach (TResult transform in selector(current))
{
yield return transform;
}
}
}

שימו לב: הוא מקבל פונקציה שממפה כל איבר לIEnumerable<TResult> ואז מחזיר את כל האיברים בכל הטרנספורמציות הנ"ל.

בנוסף, יש לו כמה overloadים, אחד מבוסס אינדקס (כמו שראינו בימים האחרונים), ואחד אחר המאפשר להגדיר גם טרנספורמציה שתתבצע בסוף עפ"י המקור והתוצאה של הטרנספורמציה, משהו כזה:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static IEnumerable<TResult> SelectMany<TSource, TCollection, TResult>
(this IEnumerable<TSource> source,
Func<TSource, IEnumerable<TCollection>> collectionSelector,
Func<TSource, TCollection, TResult> resultSelector)
{
foreach (TSource current in source)
{
foreach (TCollection transform in collectionSelector(current))
{
yield return resultSelector(current, transform);
}
}
}

המשך יום שפה מובנית שאילתא טוב

שתף