IEnumerable上的替换,插入,删除操作

时间:2016-12-29 16:58:57

标签: c# .net linq ienumerable

我有一个只接受专有的不可变集合类型的库。我希望有一个函数接受其中一个集合,并通过返回包含所做更改的新集合对此集合执行一些更改。

我想使用LINQ语法,而不是将此集合复制到List并返回。

添加操作对我来说很容易:将可枚举与另一个连接起来。 但是呢(在给定的索引处,返回给定的值而不是IEnumerable的值),Insert(在给定索引处,返回给定值然后继续迭代IEnumerable)或Delete(在给定索引处,跳过IEnumerable的值)?

.NET框架或其他库中是否提供此类功能?如果没有,我将如何实现这些功能?

3 个答案:

答案 0 :(得分:7)

您可以为这些操作制作自己的扩展程序:

  • 添加

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

    或:

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

    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++;
        }
    }
    

    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 });
    }
    
  • 替换

    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++;
        }
    }
    

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

    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++;
        }
    }
    

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

然后你可以这样打电话:

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

答案 1 :(得分:1)

问题有点宽泛,所以我将展示Replace方法的可能性。框架中没有方法可以在IEnumerable替换某些内容,因为IEnumerable应该表示不可变的序列。

这是一种简单的方法来返回带有替换元素的新IEnumerable

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);
    }
}

这将遍历源序列并返回除Equal oldValue之外的源元素。请注意,这使用==运算符,其工作原理取决于T的类型参数。

另请注意,这使用延迟执行。仅当您开始枚举结果IEnumerable时,才会枚举源序列。因此,如果在调用Replace后更改源序列,结果序列也将产生此更改。

InsertDelete的实现也很简单,但您需要计算源序列中的索引。

答案 2 :(得分:1)

根据定义,

IEnumerable是给定类型的元素的不可变的可枚举集合。不可变意味着您无法直接修改它,并且您始终必须创建一个新实例。

但是,您可以使用yield关键字来实现此行为,最好使用扩展方法。

例如,替换可能如下所示:

public static IEnumerable<T> ReplaceAt<T>(this IEnumerable<T> collection, int index, T item)
{
    var currentIndex = 0;
    foreach (var originalItem in collection)
    {
        if (currentIndex != index)
        {
            //keep the original item in place
            yield return originalItem;
        }
        else
        {
            //we reached the index where we want to replace
            yield return item;
        }
        currentIndex++;
    }
}