对 IEnumerable 的替换、插入、删除操作
Replace, Insert, Delete operations on IEnumerable
我有一个只接受专有的不可变集合类型的库。我想要一个接受这些集合之一的函数,并通过 returning 一个包含所做更改的新集合来对该集合执行一些更改。
我想使用 LINQ 语法,而不是将此集合复制到列表并返回。
添加操作对我来说很容易:将可枚举与另一个可枚举相连接。
但是 Replace(在给定的索引处,return 给定的值而不是 IEnumerable 的值)、Insert(在给定的索引处,return 给定的值,然后继续遍历 IEnumerable)或 Delete (在给定索引处,跳过 IEnumerable 的值)?
.NET 框架或其他库中是否有类似的功能?如果没有,我将如何实现这些功能?
这个问题有点宽泛,所以我将演示 Replace
方法的可能性。在 替换 中 IEnumerable
的东西的框架中没有方法,因为 IEnumerable
应该代表一个不可变的序列。
所以 return 一个带有替换元素的新 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);
}
}
这将遍历源序列和 return 源元素,Equal
oldValue
除外。请注意,这使用 ==
运算符,其工作方式取决于 T
.
的类型参数
另请注意,这使用了延迟执行。仅当您开始枚举结果 IEnumerable
时才枚举源序列。因此,如果您在调用 Replace
后更改源序列,生成的序列也会产生此更改。
Insert
和 Delete
的实现也很简单,尽管您需要计算源序列中的索引。
您可以为这些操作制作自己的扩展:
添加
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
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++;
}
}
对于实用的、更 LINQ-ey 感觉的解决方案,灵感来自 Arturo 的回答:
public static IEnumerable<T> Replace<T>(this IEnumerable<T> enumerable, Func<T, bool> selector, T value)
{
foreach (var item in enumerable)
{
yield return selector(item) ? value : item;
}
}
这样使用:var newEnumerable = oldEnumerable.Replace(x => x.Id == 1, newItem)
我有一个只接受专有的不可变集合类型的库。我想要一个接受这些集合之一的函数,并通过 returning 一个包含所做更改的新集合来对该集合执行一些更改。
我想使用 LINQ 语法,而不是将此集合复制到列表并返回。
添加操作对我来说很容易:将可枚举与另一个可枚举相连接。 但是 Replace(在给定的索引处,return 给定的值而不是 IEnumerable 的值)、Insert(在给定的索引处,return 给定的值,然后继续遍历 IEnumerable)或 Delete (在给定索引处,跳过 IEnumerable 的值)?
.NET 框架或其他库中是否有类似的功能?如果没有,我将如何实现这些功能?
这个问题有点宽泛,所以我将演示 Replace
方法的可能性。在 替换 中 IEnumerable
的东西的框架中没有方法,因为 IEnumerable
应该代表一个不可变的序列。
所以 return 一个带有替换元素的新 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);
}
}
这将遍历源序列和 return 源元素,Equal
oldValue
除外。请注意,这使用 ==
运算符,其工作方式取决于 T
.
另请注意,这使用了延迟执行。仅当您开始枚举结果 IEnumerable
时才枚举源序列。因此,如果您在调用 Replace
后更改源序列,生成的序列也会产生此更改。
Insert
和 Delete
的实现也很简单,尽管您需要计算源序列中的索引。
您可以为这些操作制作自己的扩展:
添加
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
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++;
}
}
对于实用的、更 LINQ-ey 感觉的解决方案,灵感来自 Arturo 的回答:
public static IEnumerable<T> Replace<T>(this IEnumerable<T> enumerable, Func<T, bool> selector, T value)
{
foreach (var item in enumerable)
{
yield return selector(item) ? value : item;
}
}
这样使用:var newEnumerable = oldEnumerable.Replace(x => x.Id == 1, newItem)