Linq RemoveFirst эквивалент - PullRequest
2 голосов
/ 07 декабря 2010

Я искал Linq RemoveFirst(Predicate<T> match), но смог найти только RemoveAll.

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

Ответы [ 3 ]

6 голосов
/ 07 декабря 2010

Как это:

list.RemoveAt(list.FindIndex(x => thingy));

Если предмет не найден, будет сгенерировано исключение.

Обратите внимание, что это не имеет ничего общего с LINQ и может быть сделано только с List<T>.

0 голосов
/ 30 августа 2014

Поскольку никто другой не предоставил его, вот мой метод расширения / enumerable, который реализует RemoveFirst (Predicate match).Суть в том, что вам нужно определить свой собственный IEnumerable для правильного отслеживания состояния - я не смог найти простой способ обойти это.

Вы можете попробовать это в .NET Fiddle здесь.

public static IEnumerable<T> RemoveFirst<T>(this IEnumerable<T> list, Func<T, bool> predicate)
{
    return new RemoveFirstEnumerable<T>(list, predicate);
}

public static IEnumerable<T> RemoveFirst<T>(this IEnumerable<T> list, T item)
{
    return RemoveFirst(list, x => Object.Equals(x, item));
}
private class RemoveFirstEnumerable<T> : IEnumerable<T>
{
    IEnumerable<T> m_Source;
    Func<T, bool> m_Predicate;

    public RemoveFirstEnumerable(IEnumerable<T> source, Func<T, bool> predicate)
    {
        m_Source = source;
        m_Predicate = predicate;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return new RemoveFirstEnumerator(m_Source, m_Predicate);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return new RemoveFirstEnumerator(m_Source, m_Predicate);
    }

    private class RemoveFirstEnumerator : IEnumerator<T>
    {
        IEnumerator<T> m_Enumerator;
        Func<T, bool> m_Predicate;
        bool m_RemovedOnce = false;
        public RemoveFirstEnumerator(IEnumerable<T> source, Func<T, bool> predicate)
        {
            m_Enumerator = source.Where(WherePredicate).GetEnumerator();
            m_Predicate = predicate;
        }

        bool WherePredicate(T current)
        {
            // terse version:
            return m_RemovedOnce || !(m_RemovedOnce = m_Predicate(current));

            // Long version
            //if (m_RemovedOnce)
            //{
            //    return true;
            //}
            //else
            //{
            //    m_RemovedOnce = Object.Equals(x, item);
            //    return !m_RemovedOnce;
            //}
        }

        public T Current
        {
            get { return m_Enumerator.Current; }
        }

        public bool MoveNext()
        {
            return m_Enumerator.MoveNext();
        }


        public void Reset()
        {
            m_Enumerator.Reset();
        }

        public void Dispose()
        {
            m_Enumerator.Dispose();
        }

        object IEnumerator.Current
        {
            get { return m_Enumerator.Current; }
        }
    }
}
0 голосов
/ 07 декабря 2010

Этот код на самом деле не «удаляет» элемент из последовательности, поскольку, как указывает @SLaks, последовательности linq доступны только для чтения, но он пропускает первое вхождение элемента, соответствующего критериям.Это не особенно эффективно, поскольку каждая операция со списком будет повторяться по списку.Это достаточно выразительно того, что вы пытаетесь достичь.В зависимости от того, сколько предметов вы ожидаете иметь в своем списке, это может быть разумным для вас.

  IEnumerable<int> x = Enumerable.Range(0, 20);
  var y = x.TakeWhile(xx => xx < 10).Concat(x.SkipWhile(xx => xx < 10).Skip(1));

  //Will write 0 1 2 ... 19, skipping 10
  foreach(int a in y)
  {
    System.Diagnostics.Debug.WriteLine(a);
  }
...