如果在LINQ中找到,如何在最后一条记录中应用包含并删除?

问题描述 投票:3回答:2

我有一个字符串列表

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

现在我想删除最后一条记录,如果它不包含使用LINQ的AAPL(符号名称)。

下面我写了我的代码,其中包含多行,但我想把它作为单行代码,

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查询中完成所有这些操作呢?

c# linq filestream
2个回答
2
投票

您可以使用三元运算符来决定要连接的尾部,如下所示。

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扩展方法的积累很难调试。


1
投票

我知道你需要简化过滤操作,从我在你的案例中看到,你只缺少一条信息(即当前项是否是枚举集合中的最后一项),这将有助于你定义你的谓词。我现在要写的东西似乎不是“简单的单行”;然而,它将是一个可重用的扩展,它将提供这条信息(以及更多),而无需执行额外的和不必要的循环或迭代。

最终产品将是:

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

我写的类似LINQ的扩展是由微软的EnumerableReferenceSource启发的:

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>提供有关每个项目的信息:

  • item:要评估的项目。
  • originalIndex:该项目在其原始IEnumerable<T>源中的索引(直接传递给RichWhere的索引)。
  • countingIndex:谓词将评估为true的该项的索引。
  • hasMoreItems:告诉这是否是原始IEnumerable<T>源中的最后一项。
© www.soinside.com 2019 - 2024. All rights reserved.