Linq: "Против" Take?

Использование Linq; как я могу сделать "противоположность" Take?

т.е. вместо получения первых n элементов, таких как

aCollection.Take(n)

Я хочу получить все, кроме последних n элементов. Что-то вроде

aCollection.Leave(n)

(Не спрашивайте, почему: -)

Изменить

Я полагаю, что я могу сделать это таким образом aCollection.TakeWhile((x, index) => index < aCollection.Count - n) Или в виде расширения

public static IEnumerable<TSource> Leave<TSource>(this IEnumerable<TSource> source, int n) 
{ 
  return source.TakeWhile((x, index) => index < source.Count() - n); 
}

Но в случае Linq to SQL или NHibernate Linq было бы неплохо, если бы сгенерированный SQL позаботился об этом и сгенерировал что-то вроде (для SQL Server/T-SQL)

SELECT TOP(SELECT COUNT(*) [email protected] FROM ATable) * FROM ATable Или какая-то другая более умная реализация SQL.

Я полагаю, что нет ничего подобного? (Но редактирование на самом деле не было частью вопроса.)

Ответы

Ответ 1

aCollection.Take(aCollection.Count() - n);

РЕДАКТИРОВАТЬ: Как замечательная информация, которая появилась в комментариях, вы можете подумать, что метод расширения IEnumerable .Count() медленный, потому что он будет перебирать все элементы. Но если реальный объект реализует ICollection или ICollection<T>, он просто использует свойство .Count, которое должно быть O (1). Таким образом, производительность не пострадает в этом случае.

Вы можете увидеть исходный код IEnumerable.Count() на TypeDescriptor.net.

Ответ 2

Я уверен, что для этого нет встроенного метода, но это можно сделать легко, привязав Reverse и Skip:

aCollection.Reverse().Skip(n).Reverse()

Ответ 3

Я не верю, что для этого есть встроенная функция.

aCollection.Take(aCollection.Count - n)

должен быть подходящим; взяв общее количество элементов в коллекции минус n, пропустите последние n элементов.

Ответ 4

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

public static class Extensions
{
   static IEnumerable<T> Leave<T>(this IEnumerable<T> items, int numToSkip)
   {
      var list = items.ToList();
      // Assert numToSkip <= list count.
      list.RemoveRange(list.Count - numToSkip, numToSkip);
      return List
   }
}


string alphabet = "abcdefghijklmnopqrstuvwxyz";
var chars = alphabet.Leave(10); // abcdefghijklmnop

Ответ 5

Сохранение с помощью IEnumerable philosphy и однократное перечисление для случаев, когда ICollection не реализовано, вы можете использовать эти методы расширения:

public static IEnumerable<T> Leave<T>(this ICollection<T> src, int drop) => src.Take(src.Count - drop);

public static IEnumerable<T> Leave<T>(this IEnumerable<T> src, int drop) {
    var esrc = src.GetEnumerator();
    var buf = new Queue<T>();
    while (drop-- > 0)
        if (esrc.MoveNext())
            buf.Enqueue(esrc.Current);
        else
            break;

    while (esrc.MoveNext()) {
        buf.Enqueue(esrc.Current);
        yield return buf.Dequeue();
    }
}