Добавление элемента в коллекцию с помощью LINQ - PullRequest
5 голосов
/ 18 ноября 2011

Я пытаюсь обработать некоторый список с помощью функционального подхода в C #.

Идея состоит в том, что у меня есть коллекция Tuple<T,double>, и я хочу изменить Item 2 некоторого элемента T.

Функциональный способ сделать это, поскольку данные являются неизменяемыми, состоит в том, чтобы взять список, отфильтровать все элементы, отличающиеся от элемента, для изменения, и добавить новый кортеж с новыми значениями..

Моя проблема в том, что я не знаю, как добавить элемент в конце.Я хотел бы сделать:

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 метода.Есть что-то еще?

Ответы [ 7 ]

7 голосов
/ 18 ноября 2011

Полагаю, вы ищете оператора 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();
}
3 голосов
/ 18 ноября 2011

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);
}
2 голосов
/ 08 апреля 2018

Кажется, что .NET 4.7.1 добавляет Append Оператор 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();
}
2 голосов
/ 20 марта 2017

Если вы хотите использовать дополнительный пакет, ознакомьтесь с MoreLinq , доступным на Nuget. Это обеспечивает новую перегрузку для 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, вам, вероятно, понравятся и многие другие новые функции!

Кроме того, как указал в обсуждении на Github-странице MoreLinq, функция

public static IEnumerable<TSource> Append<TSource>(this IEnumerable<TSource> source, TSource element);

с другим именем, но с той же функциональностью, доступной в .NET Core, поэтому возможно, что в будущем мы увидим его для C #.

1 голос
/ 13 апреля 2015

Один из способов - использовать .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;
    }
}
1 голос
/ 18 ноября 2011

Это должно делать то, что вы хотите (хотя он использует мутацию внутри, он чувствует себя функциональным с точки зрения вызывающего абонента):

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, как упомянуто Одедом:

public static class EnumerableEx {
  public static IEnumerable<T> Append<T>(this IEnumerable<T> source, T item) {
    return source.Concat(new T[] { item });
  }
}
0 голосов
/ 18 ноября 2011

Ближайший ответ, который я смог найти, пришел от этого поста и это:

return collection.Where(x=>!x.Item1.Equals(term)).Concat(new[]{Tuple.Create(term,value)});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...