Omer van Kloeten's .NET Zen

Programming is life, the rest is mere details

News

Omer van Kloeten's Facebook profile

Get Firefox

.NET Resources

Articles :: CodeDom

Articles :: nGineer

Culture

Projects

Extension Methods Roundup: Remove, Aggregate, At, AsIndexed and Friends

Hey hey hey! It's time for another Extension Methods Roundup! Here are some of the extension methods I've written since the last one:

Dictionary's Missing Remove Methods

public static void Remove<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TValue value)
{
// Check to see that dictionary is not null
if (dictionary == null)
throw new ArgumentNullException("dictionary");

foreach (var key in (from pair in dictionary
where EqualityComparer<TValue>.Default.Equals(value, pair.Value)
select pair.Key).ToArray())
{
dictionary.Remove(key);
}
}

public static void RemoveRange<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, IEnumerable<TValue> values)
{
// Check to see that dictionary is not null
if (dictionary == null)
throw new ArgumentNullException("dictionary");

// Check to see that values is not null
if (values == null)
throw new ArgumentNullException("values");

foreach (var value in values.ToArray())
{
ExtensionMethods.Remove(dictionary, value);
}
}

public static void RemoveRange<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, IEnumerable<TKey> keys)
{
// Check to see that dictionary is not null
if (dictionary == null)
throw new ArgumentNullException("dictionary");

// Check to see that keys is not null
if (keys == null)
throw new ArgumentNullException("keys");

foreach (var key in keys.ToArray())
{
dictionary.Remove(key);
}
}

String Aggregation

public static string Aggregate(this IEnumerable<string> enumeration, string separator)
{
return Aggregate(enumeration, str => str, separator);
}

public static string Aggregate<T>(this IEnumerable<T> enumeration, Func<T, string> toString, string separator)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");

// Check to see that toString is not null
if (toString == null)
throw new ArgumentNullException("toString");

// Check to see that separator is not null or an empty string
if (string.IsNullOrEmpty(separator))
throw new ArgumentNullException("separator");

return enumeration.Aggregate(string.Empty,
(accum, item) => string.Format("{0}{1}{2}", accum, separator, toString(item)),
str => str.Length > separator.Length ? str.Substring(separator.Length) : str);
}

Those are very good for when you want to create strings such as "a, b, c, d".

LastOrDefault

public static T LastOrDefault<T>(this IList<T> list)
{
// Check to see that list is not null
if (list == null)
throw new ArgumentNullException("list");

if (list.Count == 0)
return default(T);

return list[list.Count - 1];
}

This is an optimized version of the original LastOrDefault for lists that allow random access.

At

public static T At<T>(this IEnumerable<T> enumeration, int index)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");

return enumeration.Skip(index).First();
}

public static IEnumerable<T> At<T>(this IEnumerable<T> enumeration, params int[] indices)
{
return At(enumeration, (IEnumerable<int>)indices);
}

public static IEnumerable<T> At<T>(this IEnumerable<T> enumeration, IEnumerable<int> indices)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");

// Check to see that indices is not null
if (indices == null)
throw new ArgumentNullException("indices");

int currentIndex = 0;

foreach (int index in indices.OrderBy(i => i))
{
while (currentIndex != index)
{
enumeration = enumeration.Skip(1);
currentIndex++;
}

yield return enumeration.First();
}
}

At provides pseudo-random access to enumerable lists, where needed. I've found use for it in a couple of places which returned indices for non-IList<T> enumerations.

SequenceEqual<T1, T2>

public static bool SequenceEqual<T1, T2>(this IEnumerable<T1> left, IEnumerable<T2> right, Func<T1, T2, bool> comparer)
{
using (IEnumerator<T1> leftE = left.GetEnumerator())
{
using (IEnumerator<T2> rightE = right.GetEnumerator())
{
bool leftNext = leftE.MoveNext(), rightNext = rightE.MoveNext();

while (leftNext && rightNext)
{
// If one of the items isn't the same...
if (!comparer(leftE.Current, rightE.Current))
return false;

leftNext = leftE.MoveNext();
rightNext = rightE.MoveNext();
}

// If left or right is longer
if (leftNext || rightNext)
return false;
}
}

return true;
}

This differs from the original SequenceEqual in that it is able to accept two different types of sequences.

AsIndexed

public static IEnumerable<KeyValuePair<int, T>> AsIndexed<T>(this IEnumerable<T> enumeration)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");

int i = 0;

foreach (var item in enumeration)
{
yield return new KeyValuePair<int, T>(i++, item);
}
}

This is when you need indices, but don't want the overhead of creating an Array<T>.

The Missing SelectMany

public static IEnumerable<T> SelectMany<T>(this IEnumerable<IEnumerable<T>> source)
{
// Check to see that source is not null
if (source == null)
throw new ArgumentNullException("source");

foreach (var enumeration in source)
{
foreach (var item in enumeration)
{
yield return item;
}
}
}

Oh, come on! Why wasn't there a parameterless SelectMany in the framework? Oh well, here's one.

ToDictionary of IGrouping

public static Dictionary<TKey, IEnumerable<TElement>> ToDictionary<TKey, TElement>(
this IEnumerable<IGrouping<TKey, TElement>> enumeration)
{
// Check to see that enumeration is not null
if (enumeration == null)
throw new ArgumentNullException("enumeration");

return enumeration.ToDictionary(item => item.Key, item => item.Cast<TElement>());
}

This is shorthand for when you want to create a dictionary from the result of GroupBy.

Comments

Michael Hart said:

How do you distinguish between removing values and keys for an IDictionary<T,T>? Might be easier to name it RemoveValue.

# June 20, 2008 12:23 AM

Omer van Kloeten said:

Haven't thought about that, actually, since I rarely ever use IDictionary<T, T>.

Thanks for the tip, I'll revise my code.

# June 20, 2008 5:41 AM

Jason Haley said:

# June 20, 2008 10:14 AM

Useful method - 4 of N « Peter Petrov’s Weblog said:

Pingback from  Useful method - 4 of N &laquo; Peter Petrov&#8217;s Weblog

# June 24, 2008 6:23 AM

Petar Petrov said:

I will suggest a much better version of the string aggregate method

# public static string Aggregate<T>(this IEnumerable<T> values, Func<T, string> toString, string separator)  

# {  

#     if (values == null)  

#     {  

#         throw new ArgumentNullException("values");  

#     }  

#     if (toString == null)  

#     {  

#         throw new ArgumentNullException("toString");  

#     }  

#  

#     StringBuilder buffer = new StringBuilder();  

#  

#     foreach (var v in values)  

#     {  

#         if (buffer.Length > 0)  

#         {  

#             buffer.Append(separator);  

#         }  

#         buffer.Append(toString(v));  

#     }  

#  

#     return buffer.ToString();  

# }

I have a blog post about this.

# June 24, 2008 10:43 AM

More Extension Methods… « HSI Developer Blog said:

Pingback from  More Extension Methods&#8230; &laquo; HSI Developer Blog

# July 1, 2008 2:39 PM

.NET Utility Libraries Galore « HSI Developer Blog said:

Pingback from  .NET Utility Libraries Galore &laquo; HSI Developer Blog

# July 15, 2008 12:49 PM

Georgi Ganchev said:

About string "aggregation". Why just don't use something like:

public static string Aggregate<T>(this IEnumerable<T> values, string separator)

{

   return string.Join(separator, values.Cast<string>().ToArray());

}

Also, I think that string.Join will do good job for IEnumerable<string> using ToArray() like:

List<string> strings = new List<string> { "a", "b", "c", "d" };

string aggregated = string.Join(",", strings.ToArray());

# July 16, 2008 5:36 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)