What other Enumerable extension methods would you like to see?

A few questions on Stack Overflow have suggested to me that there might be some bits missing from LINQ to Objects. There’s the idea of a “zip” operator, which pairs up two sequences, for instance. Or the ability to apply the set operators with projections, e.g. DistinctBy, IntersectBy etc (mirroring OrderBy). They’re easy to implement, but it would be nice to get a list of what people would like to see.

So, what’s missing?

23 thoughts on “What other Enumerable extension methods would you like to see?”

  1. It’s not a part of LINQ to Objects, but I’ve always missed a method in Enumerable to just make an IEnumerable with one object. Sometimes you want to Concat a chain of items together and one or several of the steps just consist of a single item; today you have to construct an array or a list with an initializer and pass that, which distracts a bit and makes you figure out the most optimal choice (array).

    I guess you could use Enumerable.Repeat(obj, 1), but that reads badly. And I guess the problem could be solved by just adding the overload Enumerable.Concat(params T[] items) which would have the compiler and JIT do a passable encapsulation for you.

    Like

  2. MinOrDefault([optional T item]) .. a wrapper for enumerable.DefaultIfEmpty().Min().

    MaxOrDefault(…) .. same.

    IndexOf(T item) .. the opposite of ElementAt().

    Do(Action) or ForEach(Action) perhaps?

    Like

  3. Zip would, of course, be very useful, but would probably require a standard Tuple class in the framework; we’ve also already implemented our own DistinctBy extension method.

    Other methods we’ve implemented in our standard utility library are:
    Any (that returns bool, and has an out parameter that returns the item that matched the predicate)
    IEnumerable Append(IEnumerable items, T item); Prepend
    ForEach(IEnumerable, Action)
    Max & Min (with custom comparers)
    HashSet ToSet(IEnumerable)

    We’ve also created extension methods on IList, such as AsReadOnly, CopyTo, FindIndex, etc.

    Like

  4. public static IEnumerable Concat(this T obj, IEnumerable tail)
    {
    yield return obj;
    foreach (var tailObj in tail)
    yield return tailObj;
    }

    // Matches “First”, but alternatively called “Head” and “Tail”.
    public static IEnumerable Rest(this IEnumerable enumerable)
    {
    using (var enumerator = enumerable.GetEnumerator())
    {
    if (enumerator.MoveNext())
    while (enumerator.MoveNext())
    yield return enumerator.Current;
    }
    }

    Like

  5. I’m not sure all these extension methods need to be standard (don’t want to pollute the namespace too much), but I’d greatly appreciate a means to filter IEnumerable which is order-aware: I want to process it like a stream of elements.

    I frequently want to split a list in a specific spot after seeing a specific element (or sequence of elements). Or, I don’t want a mere where clause filter, I want to split the stream: basically an elementwise switch statement. Doing this in LINQ now means repeating the same query several times using a different where clause for each switch case (slow). The alternative isn’t attractive either, since a custom implementation will need to fight LINQ’s pull semantics: if you split an Enumerable, and then iterate over one branch, you’ll need to decide what to do with the other branch – cache it? discard and rerun?

    Going into an even more speculative realm, a hypothetical LINQ for true element streams would be even fancier if they supported a regular expression kind of syntax backed by an NFA/DFA, for simple yet efficient expression of trivial context-dependent processing.

    Like

  6. @Eamon – have you looked at Push LINQ (MiscUtil)? It would seem to do most of what you want.

    @Bradley – your Min/Max are already in MiscUtil (EnumerableExt)

    @Brandon – isn’t Rest the same as .Skip(1)?

    @Jon – another one from SO: perhaps an AddRange(this ICollection x, IEnumerable y)

    Like

  7. @John S – Actually easier than that SO answer makes it look. Would be trivial to add to MiscUtil (which I suspect is what Jon intends…).

    Like

  8. My favorite missing function is Iterate

    public class Pair {
    public int Index;
    public T Value;
    public Pair(int i, T v) {
    Index = i;
    Value = v;
    }
    }
    static IEnumerable<Pair> Iterate(this IEnumerable source) {
    int index = 0;
    foreach (var cur in source) {
    yield return new Pair(index, cur);
    index++;
    }
    }

    Like

  9. I don’t know the right name for these overloaded extensions, but I’d love to see something like:

    IEnumerable Slice(this IEnumerable seq, Func) { }

    IEnumerable Slice(this IEnumerable seq, Func) { }

    etc…

    Like

  10. There are hot discussions about why .NET 3.5 doesn’t have ForEach in class Enumerable.

    Even though there are several reasons/excuses stated
    1) List.ForEach always shade extension method;
    2) Enumerable methods should be chainable

    I still would like to have ForEach in Enumerable.

    Like

  11. @Morgan – re point 2, it still could be…

    public static IEnumerable ForEach(this IEnumerable source, Action action) {
    foreach (T t in source) {
    action(t);
    yield return t;
    }
    }

    Not sure if it is a great idea, but as a fluent API it seems fine (and still respects read-once streams without buffering).

    Like

  12. @Marc:
    Having implemented a chained foreach, I don’t think it’s a good idea. It leads to some very un-obvious behavior that’s difficult to debug.

    In any event, of course, foreach is in the Parallel Extensions. At least, for parallelizeable applications of foreach (and if they’re not, perhaps you should consider refactoring them).

    @Jacob:
    I seem to recall seeing an ArraySlice internal to WPF. I could be wrong. But I think I’d rather see that than a direct enumeration. Further, since slices preclude indexes, I’d strongly suggest favoring IList rather than IEnumerable in this case.

    class Slice: IList
    {
    public Slice(
    IEnumerable source,
    int startIndex,
    int maxItems)
    { … }

    List cache;

    // implementation of IList doesn’t bother enumerating until it needs to, then only enumerates as far as the highest index it needs.
    // In the case of an IList as input, it can do direct indexing rather than enumeration.
    }

    Like

  13. What I would like to see?

    Converters between streams, event sequences, and enumerables. I’ve written them to handle serial ports protocols, and they’re not fun.

    class StreamSource
    {
    Stream Input;
    Action Aggregator;
    event EventHandler Output;
    }

    class EventSource: IEnumerable
    {
    Func projector;

    // listens to one or more EventHandler,
    // and buffers a projection
    // of the event args
    }

    Like

  14. I’ve been trying to code some F# in C#, and ended up rolling the following ‘missing’ links:
    MapI, FoldI.

    A while ago I was also surprised not to find ForEach in IEnumerable, but having rolled my own, I was even more surprised when nothing happened! Having thought further, I decided on two extension methods:
    public static void Iter(this IEnumerable input, Action action)
    {
    input.ToList().ForEach(action);
    }
    (I consider this to be justified, on the basis of Seq.iter)

    And this ‘pass-through’ extension (which I’m slightly less easy with, I first thought of calling it ‘Log’, but SideEffects is a better warning):
    public static IEnumerable SideEffects(this IEnumerable items, Action pPerfomAction)
    {
    foreach (T item in items)
    {
    pPerfomAction(item);
    yield return item;
    }
    }

    Then there are these two: http://stackoverflow.com/questions/336775/pipe-forwards-in-c

    Like

  15. From my own extensions project on Codeplex – NExtension:

    Clump – Reverses SelectMany by clumping a flat list into list of lists of a fixed size.

    AtLeast – Similar to Any, but can check for “at least x” number of items instead of just 1.

    AtMost – Same as AtLeast, but for All. Checks for “at most x” number of items.

    These and Zip are probably the most useful. Feel free to use.

    Like

Leave a comment