Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace, Insert, Delete operations on IEnumerable

I have a library that only accepts a proprietary immutable collection type. I would like to have a function that accepts one of these collections and performs some changes to this collection by returning a new collection that contains the changes made.

I would like to use a LINQ syntax instead of copying this collection to a List and back.

Add operations is easy for me: concat the enumerable with another one. But what about Replace (at the given index, return the value given instead of that IEnumerable's value), Insert (at given index, return the given value and then continue iterating over the IEnumerable) or Delete (at given index, skip the IEnumerable's value)?

Are there functions like this available in the .NET framework or in another library? If not, how would I go about implementing these functions?

like image 381
Simon Farshid Avatar asked Dec 29 '16 16:12

Simon Farshid


People also ask

What is IEnumerable <> in C#?

IEnumerable is an interface defining a single method GetEnumerator() that returns an IEnumerator interface. It is the base interface for all non-generic collections that can be enumerated. This works for read-only access to a collection that implements that IEnumerable can be used with a foreach statement.

What is difference between IEnumerable and list in C#?

The main difference between IEnumerable and List in C# is that IEnumerable is an interface, while List is a concrete class. Moreover, IEnumerable is read-only and List is not.

How do I clear an IEnumerable?

Clear() will empty out an existing IEnumerable. model. Categories = new IEnumerable<whatever>() will create a new empty one. It may not be a nullable type - that would explain why it can't be set to null.

Does List implement IEnumerable?

List implements IEnumerable, but represents the entire collection in memory. LINQ expressions return an enumeration, and by default the expression executes when you iterate through it using a foreach, but you can force it to iterate sooner using .


2 Answers

You can make your own extensions for these operations:

  • Add

    public static IEnumerable<T> Add<T>(this IEnumerable<T> enumerable, T value)
    {
        foreach (var item in enumerable)
            yield return item;
    
        yield return value;
    }
    

    or:

    public static IEnumerable<T> Add<T>(this IEnumerable<T> enumerable, T value)
    {
        return enumerable.Concat(new T[] { value });
    }
    
  • Insert

    public static IEnumerable<T> Insert<T>(this IEnumerable<T> enumerable, int index, T value)
    {
        int current = 0;
        foreach (var item in enumerable)
        {
            if (current == index)
                yield return value;
    
            yield return item;
            current++;
        }
    }
    

    or

    public static IEnumerable<T> Insert<T>(this IEnumerable<T> enumerable, int index, T value)
    {
        return enumerable.SelectMany((x, i) => index == i ? new T[] { value, x } : new T[] { x });
    }
    
  • Replace

    public static IEnumerable<T> Replace<T>(this IEnumerable<T> enumerable, int index, T value)
    {
        int current = 0;
        foreach (var item in enumerable)
        {
            yield return current == index ? value : item;
            current++;
        }
    }
    

    or

    public static IEnumerable<T> Replace<T>(this IEnumerable<T> enumerable, int index, T value)
    {
        return enumerable.Select((x, i) => index == i ? value : x);
    }
    
  • Remove

    public static IEnumerable<T> Remove<T>(this IEnumerable<T> enumerable, int index)
    {
        int current = 0;
        foreach (var item in enumerable)
        {
            if (current != index)
                yield return item;
    
            current++;
        }
    }
    

    or

    public static IEnumerable<T> Remove<T>(this IEnumerable<T> enumerable, int index)
    {
        return enumerable.Where((x, i) => index != i);
    }
    

Then you can make calls like this:

IEnumerable<int> collection = new int[] { 1, 2, 3, 4, 5 };

var added = collection.Add(6);              // 1, 2, 3, 4, 5, 6
var inserted = collection.Insert(0, 0);     // 0, 1, 2, 3, 4, 5
var replaced = collection.Replace(1, 22);   // 1, 22, 3, 4, 5 
var removed = collection.Remove(2);         // 1, 2, 4, 5
like image 56
Arturo Menchaca Avatar answered Oct 12 '22 09:10

Arturo Menchaca


The question is a little broad, so I'll demonstrate a possibility for a Replace method. There are no methods for that in the framework that replace something in an IEnumerable, as IEnumerable should represent an immutable sequence.

So a naive way to return a new IEnumerable with replaced elements:

public static class Extensions
{
    public static IEnumerable<T> Replace<T>(this IEnumerable<T> source, T oldValue, T newValue)
    {
        return source.Select(element => element == oldValue ? newValue : element);
    }
}

This will iterate through the source sequence and return the source elements except for those who Equal the oldValue. Note that this uses the == operator and how this works depends on the type argument for T.

Also note that this uses deferred execution. The source sequence is only enumerated when you start to enumerate the resulting IEnumerable. So if you change the source sequence after a call to Replace, the resulting sequence will yield this change, too.

Implementations for Insert and Delete are straight forward, too, though you'll need to count an index in the source sequence.

like image 44
René Vogt Avatar answered Oct 12 '22 08:10

René Vogt