LP on .NET

February 11, 2011

Using LINQ to Create a Dictionary

Filed under: .NET,C#,Software Development — Larry Parker @ 10:22 pm

Often when I use LINQ to run a query against my data (whether objects, SQL or XML) I call .ToList() or .ToArray() at the end to execute the query and get a full in-memory representation of the result set.

Less frequently, but still useful at times, I will call .ToDictionary() to convert the collection into a Dictionary<TKey, TValue>.  For example, say we have a Customer class:

public class Customer
{
    public Int32 CustomerId { get; set; }
    public String FirstName { get; set; }
    public String LastName { get; set; }
}

We could write a test like this:

[TestMethod]
public void DictionaryTest()
{
    Customer[] customers =
    {
        new Customer { CustomerId = 1001, FirstName = "Bill", LastName = "Smith" },
        new Customer { CustomerId = 1002, FirstName = "Mary", LastName = "Jones" },
        new Customer { CustomerId = 1003, FirstName = "Joe", LastName = "Thomas" }
    };

    Dictionary<Int32, Customer> dict = customers.ToDictionary<Customer, Int32>(o => o.CustomerId);
    Assert.AreEqual("Smith", dict[1001].LastName);
    Assert.AreEqual("Jones", dict[1002].LastName);
    Assert.AreEqual("Thomas", dict[1003].LastName);
}

The key of the dictionary is the customer ID (an integer), and each dictionary element contains the customer itself.  This is a great way to quickly convert a collection of elements into a keyed lookup that returns a particular object from a collection.

But what if we want our lookup to return something other than the full object?  Say we need to create a dictionary keyed by customer ID that returns the first and last name as a single string?

We could do something like this:

public Dictionary<Int32, String> GetCustomerDict(IEnumerable<Customer> customers)
{
    Dictionary<Int32, String> dict = new Dictionary<Int32, String>();

    foreach (Customer customer in customers)
        dict.Add(customer.CustomerId, customer.FirstName + " " + customer.LastName);

    return dict;
}

Then we could change our test like this:

[TestMethod]
public void DictionaryTest()
{
    Customer[] customers =
    {
        new Customer { CustomerId = 1001, FirstName = "Bill", LastName = "Smith" },
        new Customer { CustomerId = 1002, FirstName = "Mary", LastName = "Jones" },
        new Customer { CustomerId = 1003, FirstName = "Joe", LastName = "Thomas" }
    };

    Dictionary<Int32, String> dict = GetCustomerDict(customers);
    Assert.AreEqual("Bill Smith", dict[1001]);
    Assert.AreEqual("Mary Jones", dict[1002]);
    Assert.AreEqual("Joe Thomas", dict[1003]);
}

The key is still the customer ID, but instead of the dictionary element returning the full customer object, it now returns a simple string containing the customer’s first and last name.  Note also that the new dictionary has no reference to the original customer objects (which is sometimes desirable).

So our GetCustomerDict method works and was just a few lines of code.

But LINQ’s .ToDictionary extension method provides another overload that makes our lives much easier.  Here’s how it looks:

.ToDictionary<TSource, TKey, TElement>

Note that this overload take three types.  TSource is the type of our collection (Customer in this example), TKey is the type of the dictionary’s key (integer), and TElement is the type of the element we want to return (string in this case).  Just what we need!  🙂

Here’s the final version of the test:

[TestMethod]
public void DictionaryTest()
{
    Customer[] customers =
    {
        new Customer { CustomerId = 1001, FirstName = "Bill", LastName = "Smith" },
        new Customer { CustomerId = 1002, FirstName = "Mary", LastName = "Jones" },
        new Customer { CustomerId = 1003, FirstName = "Joe", LastName = "Thomas" }
    };

    Dictionary<Int32, String> dict = customers.ToDictionary<Customer, Int32, String>(k => k.CustomerId,
        e => e.FirstName + " " + e.LastName);

    Assert.AreEqual("Bill Smith", dict[1001]);
    Assert.AreEqual("Mary Jones", dict[1002]);
    Assert.AreEqual("Joe Thomas", dict[1003]);
}

One line of code does the trick.  The power of LINQ!  🙂

Blog at WordPress.com.