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();
}
}