281. Lookup, ToLookup

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

דרך אחת לעשות זאת היא להגדיר פשוט Dictionary שהערכים שלו הם מסוג Collection מסוים, למשל:

1
2
IDictionary<string,ICollection<int>> nameToIds =
new Dictionary<string,ICollection<int>>();

ואז להשתמש בכל מיני דרכים שיאפשרו לנו שימוש נוח איתו: ראו למשל טיפ מספר 13 וטיפ מספר 73.

החל מFramework 3.5, קיים הממשק ILookup המגדיר מיפוי כזה:

1
2
3
4
5
6
public interface ILookup<TKey, TElement> : IEnumerable<IGrouping<TKey, TElement>>
{
int Count { get; }
IEnumerable<TElement> this[TKey key] { get; }
bool Contains(TKey key);
}

בואו נשים לב לכמה דברים: הממשק מממשIEnumerable של IGrouping (ראו גם טיפ מספר 115) – הדבר הזה מאפשר לנו לרוץ על כל איברי הLookupבאופן אנלוגי לאופן בו KeyValuePair מאפשר לנו לרוץ על כל איברי הDictionary (ראו גם טיפ מספר 17)

בנוסף, קיים פה Indexer המחזיר לנוIEnumerable<TElement> - כלומר המיפוי אכן ממפה את המפתחות שלנו לאוספים. שימו לב שזה לאICollection<TElement>. בנוסף אין כאן מתודתAdd, או לחלופין Setter לIndexer. למעשה, המבנה הזה לא מיועד להיות בר שינוי, אלא להיות Immutable (אי אפשר להוסיף לאוסף איבר, ואי אפשר למפות מפתח לאוסף מסוים – ראו גם טיפ מספר 271)

אז איך משתמשים בזה? קיים מימוש של הFramework בשם הלא מפתיע Lookup. הבעיה היחידה היא שהConstructorשל Lookup הוא private.

דרך אחת שבה ניתן להשתמש בזה היא ע"י הExtension Method בשם ToLookup: בדומה למה שראינו בפעם שעברה, הExtension Method מקבל Delegate שבוחר את הKey וDelegate שבוחר את הValue וממפה אותם:

1
2
ILookup<string, int> nameToIds =
people.ToLookup(x => x.Name, x => x.Id);

כמו פעם קודמת, הדבר יכול להיות מאוד שימושי ע"י שילוב Anonymous Types כדי לבצע עיבוד מתוחכם על מידע.

סופ"ש רב ערכי טוב!

שתף