使用 LINQ 将元素追加到集合
本文关键字:集合 追加 元素 LINQ 使用 | 更新日期: 2023-09-27 18:15:10
我正在尝试用 C# 中的函数式方法处理一些列表。
这个想法是我有一个Tuple<T,double>
集合,我想更改某些元素T
的Item 2
。
由于数据是不可变的,因此这样做的功能方法是获取列表,过滤元素与要更改的元素不同的所有元素,并使用新值附加一个新元组。
我的问题是我不知道如何在末尾附加元素。我想做:
public List<Tuple<T,double>> Replace(List<Tuple<T,double>> collection, T term,double value)
{
return collection.Where(x=>!x.Item1.Equals(term)).Append(Tuple.Create(term,value));
}
但是没有Append
方法。还有别的吗?
您正在寻找Concat
运算符。
它将两个IEnumerable<T>
联接在一起,因此您可以创建一个包含单个要联接的项目。
public List<Tuple<T,double>> Replace(List<Tuple<T,double>> collection, T term,double value)
{
var newItem = new List<Tuple<T,double>>();
newItem.Add(new Tuple<T,double>(term,value));
return collection.Where(x=>!x.Item1.Equals(term)).Concat(newItem).ToList();
}
似乎 .NET 4.7.1 添加了追加 LINQ 运算符,这正是您想要的。与Concat不同,它采用单个值。
顺便说一下,如果你声明一个泛型方法,你应该在其名称后面包含类型参数:
public List<Tuple<T, double>> Replace<T>(List<Tuple<T, double>> collection, T term, double value)
{
return collection.Where(x => !x.Item1.Equals(term))
.Append(Tuple.Create(term, value))
.ToList();
}
LINQ 不用于突变。函数式编程避免突变。
因此:
public IEnumerable<Tuple<T,double>> Extend(IEnumerable<Tuple<T,double>> collection,
T term,double value)
{
foreach (var x in collection.Where(x=>!x.Item1.Equals(term)))
{
yield return x;
}
yield return Tuple.Create(term,value);
}
如果您愿意使用其他软件包,请查看Nuget上提供的MoreLinq。这为 Concat
-函数提供了新的重载:
public static IEnumerable<T> Concat<T>(this IEnumerable<T> head, T tail);
此功能完全符合要求,例如,您可以执行
var myEnumerable = Enumerable.Range(10, 3); // Enumerable of values 10, 11, 12
var newEnumerable = myEnumerable.Concat(3); // Enumerable of values 10, 11, 12, 3
而且,如果您喜欢 LINQ,您可能也会喜欢许多其他新功能!
此外,正如在MoreLinq Github页面上的讨论中所指出的那样,该功能
public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element);
具有不同的名称,但在 .NET Core 中提供了相同的功能,因此将来我们可能会在 C# 中看到它。
这应该做你想要的(虽然它在内部使用了突变,但从调用者的角度来看它感觉很实用(:
public List<Tuple<T, double>> Replace(List<Tuple<T, double>> collection, T term, double value) {
var result = collection.Where(x => !x.Item1.Equals(term)).ToList();
result.Add(Tuple.Create(term, value));
return result;
}
另一种方法是使用"map"(select
LINQ 中的
public List<Tuple<T, double>> Replace(List<Tuple<T, double>> collection, T term, double value) {
return collection.Select(x =>
Tuple.Create(
x.Item1,
x.Item1.Equals(term) ? value : x.Item2)).ToList();
}
但它可能会给你带来与你最初意图不同的结果。虽然,对我来说,当我看到一种叫做Replace
的方法时,这就是我的想法,即就地替换。
更新
您也可以像这样创建所需的内容:
public List<Tuple<T, double>> Replace(List<Tuple<T, double>> collection, T term, double value) {
return collection.
Where(x => !x.Item1.Equals(term)).
Append(Tuple.Create(term, value)).
ToList();
}
使用Concat
,如Oded所述:
public static class EnumerableEx {
public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T item) {
return source.Concat(new T[] { item });
}
}
一种方法是使用 .Concat()
,但你需要有一个可枚举的项目而不是单个项目作为第二个参数。使用单个元素创建数组确实有效,但编写起来很麻烦。
最好编写一个自定义扩展方法来执行此操作。
一种方法是创建一个新List<T>
,然后添加第一个列表中的项目,然后添加第二个列表中的项目。但是,最好改用 yield
-关键字,这样您就不需要创建列表,并且将以惰性方式计算可枚举项:
public static class EnumerableExtensions
{
public static IEnumerable<T> Concat<T>(this IEnumerable<T> list, T item)
{
foreach (var element in list)
{
yield return element;
}
yield return item;
}
}
我能找到的最接近的答案来自这篇文章,是:
return collection.Where(x=>!x.Item1.Equals(term)).Concat(new[]{Tuple.Create(term,value)});