UPDATE: If you've previously read this post and had troubles compiling the code below, the errors have been fixed. When I paste my code into my blog's text editor, sometimes things go missing and sometimes I do not notice those things. In the examples below, the generic methods were missing the generic types after the method names. Sorry again!
The extension methods within the System.LINQ namespace provide an excellent opportunity to program in more of a declarative style. The syntax may be awkward at first but the conciseness and readability these methods help achieve makes them worthwhile, even if it makes debugging more difficult. My favorite though has to be AddRange, which allows me to add a collection of data to another collection. I find myself needing to fill a collection with data based on a query on another collection quite frequently and this method allows me to do so without the oh so boring iterate/test/add loop:
foreach (var i in c)
if (i.Rank == "Developer")
{
e.Add(i);
}
I used to write code like this quite a bit. Let's see how the above would look using the List.AddRange method:
e.AddRange(c.Where(i => i.Rank == "Developer"));
Much easier on the eyes, isn't it? Occasionally I find myself needing to add filtered data to other types of collections, including the generic Dictionary collection. Unfortunately, the Dictionary class (and the IDictionary interface) does not have an AddRange extension method. I wrote two versions of this method for my own use and thought I'd share them. The first version of this method adds an IEnumerable of KeyValuePair's to a collection implementing the IDictionary interface.
static void AddRange<T, U>(this IDictionary<T, U> D, IEnumerable<KeyValuePair<T, U>> V)
{
foreach (var kvp in V)
{
if (D.ContainsKey(kvp.Key))
{
throw new ArgumentException("An item with the same key has already been added.");
}
D.Add(kvp);
}
}
Which you can then call like this:
var employees = new Dictionary<int, string>();
employees.AddRange(employeelist.Select(e => new KeyValuePair<int, string>(e.ID, e.Name)));
In the above example, an exception will be thrown if two employees with the same ID are found in the employeelist collection. Another version of AddRange I find useful is one which does not throw an exception when a duplicate key is found, but rather adds this keys' value to the collection associated with the key. This forms some what of an association list, although not in the strictest definition (or perhaps even the loosest). Here is how this looks:
static void AddRange<T, U>(this IDictionary<T, List<U>> D, IEnumerable<KeyValuePair<T, U>> V)
{
foreach (var kvp in V)
{
if (!D.ContainsKey(kvp.Key))
{
D.Add(kvp.Key, new List<U>());
}
List<U> c = D[kvp.Key];
if (c == null)
{
c = new List<U>();
D[kvp.Key] = c;
}
c.Add(kvp.Value);
}
}
If you're curious about the seemingly goofy logic surrounding the possibility of a null list in the dictionary object, the reason for it is because the dictionary could be constructed with a null list for a given key prior to the invocation of this extension method. This method can then be called like this:
var ranks = new Dictionary<string, List<string>>();
ranks.AddRange(ranklist.Select(e => new KeyValuePair<string, string>(e.Rank, e.Name)));
When a duplicate value for e.Rank is found, the method will add e.Name to the List at this key. If this was in fact a "true" association list, e.Name would be added to the beginning of the list instead of the end but that's just being picky.
Aside: If you happened to read my last post on memoizing functions written in CPS, you may have questioned the odd use of for loops in the Memoize function. How could an association list be used to make the semantics more clear?
Anyways, I hope you find these methods as useful as I have. Feel free to use/change/evolve them to fit your needs since they were written to fit mine.
