Как взять всех кроме последнего элемента в последовательности с помощью LINQ?

Из ссылки svrist:

первый объект в http://java.sun.com/performance/jvmstat/faq.html упоминания опция, которую можно выключить для отключения целого комплекта функций:-XX:-UsePerfData.

120
задан Vikas Gupta 2 January 2015 в 22:29
поделиться

5 ответов

Я не знаю решения Linq. Но вы можете легко запрограммировать алгоритм самостоятельно, используя генераторы (yield return).

public static IEnumerable<T> TakeAllButLast<T>(this IEnumerable<T> source) {
    var it = source.GetEnumerator();
    bool hasRemainingItems = false;
    bool isFirst = true;
    T item = default(T);

    do {
        hasRemainingItems = it.MoveNext();
        if (hasRemainingItems) {
            if (!isFirst) yield return item;
            item = it.Current;
            isFirst = false;
        }
    } while (hasRemainingItems);
}

static void Main(string[] args) {
    var Seq = Enumerable.Range(1, 10);

    Console.WriteLine(string.Join(", ", Seq.Select(x => x.ToString()).ToArray()));
    Console.WriteLine(string.Join(", ", Seq.TakeAllButLast().Select(x => x.ToString()).ToArray()));
}

Или как обобщенное решение, отбрасывающее последние n элементов (с использованием очереди, подобной предложенной в комментариях):

public static IEnumerable<T> SkipLastN<T>(this IEnumerable<T> source, int n) {
    var  it = source.GetEnumerator();
    bool hasRemainingItems = false;
    var  cache = new Queue<T>(n + 1);

    do {
        if (hasRemainingItems = it.MoveNext()) {
            cache.Enqueue(it.Current);
            if (cache.Count > n)
                yield return cache.Dequeue();
        }
    } while (hasRemainingItems);
}

static void Main(string[] args) {
    var Seq = Enumerable.Range(1, 4);

    Console.WriteLine(string.Join(", ", Seq.Select(x => x.ToString()).ToArray()));
    Console.WriteLine(string.Join(", ", Seq.SkipLastN(3).Select(x => x.ToString()).ToArray()));
}
57
ответ дан 24 November 2019 в 01:41
поделиться

Ничего в BCL (или, как мне кажется, MoreLinq), но вы можно создать свой собственный метод расширения.

public static IEnumerable<T> TakeAllButLast<T>(this IEnumerable<T> source)
{
    using (var enumerator = source.GetEnumerator())
        bool first = true;
        T prev;
        while(enumerator.MoveNext())
        {
            if (!first)
                yield return prev;
            first = false;
            prev = enumerator.Current;
        }
    }
}
12
ответ дан 24 November 2019 в 01:41
поделиться

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

var result = sequence.Reverse().Skip(1);
43
ответ дан 24 November 2019 в 01:41
поделиться

Поскольку я не поклонник явного использования Enumerator , вот альтернатива. Обратите внимание, что методы-оболочки необходимы для того, чтобы позволить недопустимым аргументам возникать раньше, а не откладывать проверки до тех пор, пока последовательность не будет фактически перечислена.

public static IEnumerable<T> DropLast<T>(this IEnumerable<T> source)
{
    if (source == null)
        throw new ArgumentNullException("source");

    return InternalDropLast(source);
}

private static IEnumerable<T> InternalDropLast<T>(IEnumerable<T> source)
{
    T buffer = default(T);
    bool buffered = false;

    foreach (T x in source)
    {
        if (buffered)
            yield return buffer;

        buffer = x;
        buffered = true;
    }
}

Согласно предложению Эрика Липперта, это легко обобщается на n элементов:

public static IEnumerable<T> DropLast<T>(this IEnumerable<T> source, int n)
{
    if (source == null)
        throw new ArgumentNullException("source");

    if (n < 0)
        throw new ArgumentOutOfRangeException("n", 
            "Argument n should be non-negative.");

    return InternalDropLast(source, n);
}

private static IEnumerable<T> InternalDropLast<T>(IEnumerable<T> source, int n)
{
    Queue<T> buffer = new Queue<T>(n + 1);

    foreach (T x in source)
    {
        buffer.Enqueue(x);

        if (buffer.Count == n + 1)
            yield return buffer.Dequeue();
    }
}

Где я теперь буфер перед уступкой вместо уступки после уступки, так что случай n == 0 не требует специальной обработки.

42
ответ дан 24 November 2019 в 01:41
поделиться

Почему бы просто не .ToList() на последовательности, затем вызвать count и take, как вы делали изначально... но поскольку он был собран в список, он не должен делать дорогое перечисление дважды. Верно?

1
ответ дан 24 November 2019 в 01:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: