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.

Advertisements

8 thoughts on “Extension Methods Roundup: Remove, Aggregate, At, AsIndexed and Friends

  1. I will suggest a much better version of the string aggregate method
    # public static string Aggregate(this IEnumerable values, Func 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.

  2. About string “aggregation”. Why just don’t use something like:
    public static string Aggregate(this IEnumerable values, string separator)
    {
    return string.Join(separator, values.Cast().ToArray());
    }
    Also, I think that string.Join will do good job for IEnumerable using ToArray() like:
    List strings = new List { “a”, “b”, “c”, “d” };
    string aggregated = string.Join(“,”, strings.ToArray());

  3. Just a note about your coding style.
    // Check to see that enumeration is not null
    Man… This line is not a comment. It is an insult to the reader’s intellect.

  4. The Enumerable.LastOrDefault method is already optimized for lists. Check the code using Reflector ;)
    Also, when you use iterator blocks, you should do it in a separate method, otherwise the argument checks will be done only when the result is enumerated…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s