使用 LINQ 将元素追加到集合

本文关键字:集合 追加 元素 LINQ 使用 | 更新日期: 2023-09-27 18:15:10

我正在尝试用 C# 中的函数式方法处理一些列表。

这个想法是我有一个Tuple<T,double>集合,我想更改某些元素TItem 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方法。还有别的吗?

使用 LINQ 将元素追加到集合

我相信

您正在寻找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)});