Как подать заявку на последнюю запись и удалить, если она найдена в LINQ? - PullRequest
0 голосов
/ 22 февраля 2019

У меня есть список строк, таких как

AAPL,28/03/2012,88.34,88.778,87.187,88.231,163682382
AAPL,29/03/2012,87.54,88.08,86.747,87.123,151551216
FB,30/03/2012,86.967,87.223,85.42,85.65,182255227

Теперь я хочу удалить только последнюю запись, если она не содержит AAPL (имя символа), используя LINQ.

Ниже я пишумой код, который содержит несколько строк, но я хочу сделать его однострочным кодом,

fileLines = System.IO.File.ReadAllLines(fileName).AsParallel().Skip(1).ToList();
var lastLine = fileLines.Last();
if (!lastLine.Contains(item.sym))
{
    fileLines.RemoveAt(fileLines.Count - 1);
}

Итак, как я могу сделать все это в однострочном запросе linq?

Ответы [ 2 ]

0 голосов
/ 23 февраля 2019

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

Конечным продуктом этого будет:

IEnumerable<string> fileLines = System.IO.File.ReadLines(fileName).RichWhere((item, originalIndex, countedIndex, hasMoreItems) => hasMoreItems || item.StartsWith("AAPL"));

LINQ-подобное расширение, которое я написал, вдохновлено Microsoft Enumerable at ReferenceSource :

public delegate bool RichPredicate<T>(T item, int originalIndex, int countedIndex, bool hasMoreItems);
public static class EnumerableExtensions
{
    /// <remarks>
    /// This was contributed by Aly El-Haddad as an answer to this Stackoverflow.com question:
    /// https://stackoverflow.com/q/54829095/3602352
    /// </remarks>
    public static IEnumerable<T> RichWhere<T>(this IEnumerable<T> source, RichPredicate<T> predicate)
    {
        return new RichWhereIterator<T>(source, predicate);
    }
    private class RichWhereIterator<T> : IEnumerable<T>, IEnumerator<T>
    {
        private readonly int threadId;
        private readonly IEnumerable<T> source;
        private readonly RichPredicate<T> predicate;
        private IEnumerator<T> enumerator;
        private int state;
        private int countedIndex = -1;
        private int originalIndex = -1;
        private bool hasMoreItems;

        public RichWhereIterator(IEnumerable<T> source, RichPredicate<T> predicate)
        {
            threadId = Thread.CurrentThread.ManagedThreadId;
            this.source = source ?? throw new ArgumentNullException(nameof(source));
            this.predicate = predicate ?? ((item, originalIndex, countedIndex, hasMoreItems) => true);
        }
        public T Current { get; private set; }

        object IEnumerator.Current => Current;

        public void Dispose()
        {
            if (enumerator is IDisposable disposable)
                disposable.Dispose();
            enumerator = null;
            originalIndex = -1;
            countedIndex = -1;
            hasMoreItems = false;
            Current = default(T);
            state = -1;
        }

        public bool MoveNext()
        {
            switch (state)
            {
                case 1:
                    enumerator = source.GetEnumerator();
                    if (!(hasMoreItems = enumerator.MoveNext()))
                    {
                        Dispose();
                        break;
                    }
                    ++originalIndex;
                    state = 2;
                    goto case 2;
                case 2:
                    if (!hasMoreItems) //last predicate returned true and that was the last item
                    {
                        Dispose();
                        break;
                    }
                    T current = enumerator.Current;
                    hasMoreItems = enumerator.MoveNext();
                    ++originalIndex;
                    if (predicate(current, originalIndex - 1, countedIndex + 1, hasMoreItems))
                    {
                        ++countedIndex;
                        Current = current;
                        return true;
                    }
                    else if (hasMoreItems)
                    { goto case 2; }

                    //predicate returned false and there're no more items
                    Dispose();
                    break;
            }
            return false;
        }

        public void Reset()
        {
            Current = default(T);
            hasMoreItems = false;
            originalIndex = -1;
            countedIndex = -1;
            state = 1;
        }

        public IEnumerator<T> GetEnumerator()
        {
            if (threadId == Thread.CurrentThread.ManagedThreadId && state == 0)
            {
                state = 1;
                return this;
            }
            return new RichWhereIterator<T>(source, predicate) { state = 1 };
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
}

RichPredicate<T>, которое можно рассматривать как Func<T, int, int, bool, bool>предоставить эту информацию о каждом элементе:

  • элемент : элемент для оценки.
  • originalIndex : индекс этого элемента в егоисходный IEnumerable<T> источник (тот, который был непосредственно передан в RichWhere).
  • counttedIndex : индекс этого элемента IF , предикат оценит как истинный.
  • hasMoreItems : указывает, будет ли это последний элемент из исходного IEnumerable<T> источника.
0 голосов
/ 22 февраля 2019

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

fileLines
    = fileLines.Take(fileLines.Count())
               .Concat(fileLines.Last().Contains(item.sym) ? Enumerable.Empty
                                                           : new string[]{ item.sym });

Вы можете сформулировать его еще более сокращенно следующим образом.

fileLines
    = System.IO.File.ReadAllLines(fileName)
                    .AsParallel()
                    .Skip(1)
                    .Take(fileLines.Count())
                    .Concat(fileLines.Last().Contains(item.sym) ? Enumerable.Empty
                                                                : new string[]{ item.sym });
                    .ToList();

При этомтакое усилие сомнительно.Накопление лениво оцененных методов расширения Linq трудно отладить.

...