189

The MSDN explains Lookup like this:

A Lookup<TKey, TElement> resembles a Dictionary<TKey, TValue>. The difference is that a Dictionary<TKey, TValue> maps keys to single values, whereas a Lookup<TKey, TElement> maps keys to collections of values.

I don't find that explanation particularly helpful. What is Lookup used for?

dan-gph
  • 16,301
  • 12
  • 61
  • 79

6 Answers6

252

It's a cross between an IGrouping and a dictionary. It lets you group items together by a key, but then access them via that key in an efficient manner (rather than just iterating over them all, which is what GroupBy lets you do).

For example, you could take a load of .NET types and build a lookup by namespace... then get to all the types in a particular namespace very easily:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;

public class Test
{
    static void Main()
    {
        // Just types covering some different assemblies
        Type[] sampleTypes = new[] { typeof(List<>), typeof(string), 
                                     typeof(Enumerable), typeof(XmlReader) };

        // All the types in those assemblies
        IEnumerable<Type> allTypes = sampleTypes.Select(t => t.Assembly)
                                               .SelectMany(a => a.GetTypes());

        // Grouped by namespace, but indexable
        ILookup<string, Type> lookup = allTypes.ToLookup(t => t.Namespace);

        foreach (Type type in lookup["System"])
        {
            Console.WriteLine("{0}: {1}", 
                              type.FullName, type.Assembly.GetName().Name);
        }
    }
}

(I'd normally use var for most of these declarations, in normal code.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 3
    If it has the best of both worlds, then why bother with a Dictionary? – Kyle Baran Mar 21 '13 at 02:30
  • 17
    @KyleBaran: Because it would be pointless for genuine key/value pair collections, where there's only a single value per key. – Jon Skeet Mar 21 '13 at 06:41
  • 17
    @KyleBaran `Lookup<,>` is simply an immutable collection (with no `Add` method for eg) which has a limited use. Furthermore, its not a general purpose collection in the sense that if you do lookup on a nonexistent key you get an empty sequence rather than an exception, which is meaningful only in special contexts, for eg, with linq. This goes well with the fact that MS hasn't provided a public constructor for the class. – nawfal May 22 '14 at 14:37
  • 1
    Reading answers order is jwg -> bobbymcr -> jonskeet – Soner from The Ottoman Empire Apr 18 '20 at 21:59
  • 1
    Careful about jwg's answer that @SonerfromTheOttomanEmpire suggests reading. It's wrong in that it misunderstands ILookup and then gives misleading advice. – ANeves Dec 16 '22 at 10:59
80

One way to think about it is this: Lookup<TKey, TElement> is similar to Dictionary<TKey, Collection<TElement>>. Basically a list of zero or more elements can be returned via the same key.

namespace LookupSample
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    class Program
    {
        static void Main(string[] args)
        {
            List<string> names = new List<string>();
            names.Add("Smith");
            names.Add("Stevenson");
            names.Add("Jones");

            ILookup<char, string> namesByInitial = names.ToLookup((n) => n[0]);

            // count the names
            Console.WriteLine("J's: {0}", namesByInitial['J'].Count()); // 1
            Console.WriteLine("S's: {0}", namesByInitial['S'].Count()); // 2
            Console.WriteLine("Z's: {0}", namesByInitial['Z'].Count()); // 0, does not throw
        }
    }
}
Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
bobbymcr
  • 23,769
  • 3
  • 56
  • 67
39

One use of Lookup could be to reverse a Dictionary.

Suppose you have a phonebook implemented as a Dictionary with a bunch of (unique) names as keys, each name associated with a phone number. But two people with different names might share the same phone number. This isn't a problem for a Dictionary, which doesn't care that two keys correspond to the same value.

Now you want a way of looking up who a given phone number belongs to. You build a Lookup, adding all the KeyValuePairs from your Dictionary, but backwards, with the value as the key and the key as the value. You can now query a phone number, and obtain a list of names of all the people whose phone number that is. Building a Dictionary with the same data would drop data (or fail, depending on how you did it), since doing

dictionary["555-6593"] = "Dr. Emmett Brown";
dictionary["555-6593"] = "Marty McFly";

means that the second entry overwrites the first - the Doc is no longer listed.

Trying to write the same data in a slightly different way:

dictionary.Add("555-6593", "Dr. Emmett Brown");
dictionary.Add("555-6593", "Marty McFly");

would throw an exception on the second line since you can't Add a key which is already in the Dictionary.

[Of course, you might want to use some other single data structure to do lookups in both directions, etc. This example means that you have to regenerate the Lookup from the Dictionary each time the latter changes. But for some data it could be the right solution.]

jwg
  • 5,547
  • 3
  • 43
  • 57
  • 2
    The answer is vital to comprehend the concept. +1. Reading answers order is jwg -> bobbymcr -> jonskeet – Soner from The Ottoman Empire Apr 18 '20 at 21:57
  • jwg, you're misunderstanding the Lookup. The equivalent of `Lookup<_, Person>` is not `Dictionary<_, Person>`, but `Dictionary<_, Bag>`. From an `ILookup`, the result of `myLookup["555-6593"]` is not a Person `"Dr. Emmett Brown"` but a set of Persons `{ "Dr. Emmett Brown" }`! So when accessing your equivalent dictionary, you can't do `dictionary.Add("555-6593", "Dr. Emmett Brown");` because it takes a list; you'd have to get-or-create the list, then add to the list. – ANeves Dec 16 '22 at 10:53
  • Maybe it's simpler like this: the reverse dictionary of the Dic that allows multiple people to have the same phone is not Dic like your answer assumes, but Dic>. And this Dic> **does** have the same data format and data relationship as a Lookup. – ANeves Dec 16 '22 at 10:56
19

I haven't successfully used it before, but here is my go:

A Lookup<TKey, TElement> would behave pretty much like a (relational) database index on a table without a unique constraint. Use it in the same places you would use the other.

Daren Thomas
  • 67,947
  • 40
  • 154
  • 200
6

I guess you could argue it this way: imagine you're creating a data structure to hold the contents of a phone book. You want to key by lastName and then by firstName. Using a dictionary here would be dangerous because many people can have the same name. So a Dictionary will always, at most, map to a single value.

A Lookup will map to potentially several values.

Lookup["Smith"]["John"] will be a collection of size one billion.

David Andres
  • 31,351
  • 7
  • 46
  • 36
  • Your answer inspired my follow-up question ["How ToLookup() with multiple indexes?"](http://stackoverflow.com/questions/15734454/how-tolookup-with-multiple-indexes). How can I reproduce such, with multiple indexes, lookup? Could you answer it possibly using any other sample or reference where it is possible to use `Lookup["Smith"]["John"] `? – Fulproof Apr 01 '13 at 03:27
3

Supplement more:
ToLookup is immediate execution, and will cache the result in memory. But GroupBy is deferred execution, will not cache grouped result, it will regroup when you called it each time.
If you need to repeatedly access a "grouped fixed data", you should choose ToLookUp to get a LookUp Instance. Especially when the amount of data is large or access data many times, using GroupBy will cause serious performance problems - ToLookUp at the cost of using more memory, the cached grouping results will give your code better performance.

BTW: LookUp can sometimes be used as a "EasyDictionary", because it doesn't throw exceptions on non existed key.