kbr*_*ton 397
collection.Skip(Math.Max(0, collection.Count() - N));
Run Code Online (Sandbox Code Playgroud)
此方法保留项目顺序,而不依赖于任何排序,并且具有跨多个LINQ提供程序的广泛兼容性.
务必注意不要拨打Skip负号.某些提供程序(如实体框架)在显示负参数时将生成ArgumentException.呼吁Math.Max避免这种整洁.
下面的类包含扩展方法的所有基本知识,包括:静态类,静态方法和this关键字的使用.
public static class MiscExtensions
{
// Ex: collection.TakeLast(5);
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int N)
{
return source.Skip(Math.Max(0, source.Count() - N));
}
}
Run Code Online (Sandbox Code Playgroud)
关于绩效的简要说明:
由于调用Count()可能导致某些数据结构的枚举,因此这种方法可能会导致两次数据传递.对于大多数可枚举来说,这不是一个真正的问题; 实际上,Lists,Arrays甚至EF查询已经存在优化,以便Count()在O(1)时间内评估操作.
但是,如果你必须使用一个只进枚举,并想避免两遍,考虑像一个通算法的Lasse五卡尔森或马克拜尔斯描述.这两种方法都使用临时缓冲区来保存枚举时的项目,一旦找到集合的末尾就会产生这些项目.
Jam*_*ran 58
coll.Reverse().Take(N).Reverse().ToList();
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> coll, int N)
{
return coll.Reverse().Take(N).Reverse();
}
Run Code Online (Sandbox Code Playgroud)
更新:解决clintp的问题:a)使用上面定义的TakeLast()方法解决了问题,但是如果你真的想要在没有额外方法的情况下做到这一点,那么你只需要认识到Enumerable.Reverse()可以是用作扩展方法,您不需要以这种方式使用它:
List<string> mystring = new List<string>() { "one", "two", "three" };
mystring = Enumerable.Reverse(mystring).Take(2).Reverse().ToList();
Run Code Online (Sandbox Code Playgroud)
ang*_*son 44
注意:我错过了你的问题标题,说使用Linq,所以我的答案实际上并没有使用Linq.
如果你想避免缓存整个集合的非懒副本,你可以写,做它用一个链表的简单方法.
以下方法将在原始集合中找到的每个值添加到链接列表中,并将链接列表修剪为所需的项目数.由于它不断修剪到这个数量的项目通过遍历集合的整个时间的链表,它只会保留一个副本从原始集合最N项.
它不需要您知道原始集合中的项目数,也不需要多次迭代它.
用法:
IEnumerable<int> sequence = Enumerable.Range(1, 10000);
IEnumerable<int> last10 = sequence.TakeLast(10);
...
Run Code Online (Sandbox Code Playgroud)
扩展方法:
public static class Extensions
{
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> collection,
int n)
{
if (collection == null)
throw new ArgumentNullException("collection");
if (n < 0)
throw new ArgumentOutOfRangeException("n", "n must be 0 or greater");
LinkedList<T> temp = new LinkedList<T>();
foreach (var value in collection)
{
temp.AddLast(value);
if (temp.Count > n)
temp.RemoveFirst();
}
return temp;
}
}
Run Code Online (Sandbox Code Playgroud)
Mar*_*ers 29
这是一个适用于任何可枚举但只使用O(N)临时存储的方法:
public static class TakeLastExtension
{
public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int takeCount)
{
if (source == null) { throw new ArgumentNullException("source"); }
if (takeCount < 0) { throw new ArgumentOutOfRangeException("takeCount", "must not be negative"); }
if (takeCount == 0) { yield break; }
T[] result = new T[takeCount];
int i = 0;
int sourceCount = 0;
foreach (T element in source)
{
result[i] = element;
i = (i + 1) % takeCount;
sourceCount++;
}
if (sourceCount < takeCount)
{
takeCount = sourceCount;
i = 0;
}
for (int j = 0; j < takeCount; ++j)
{
yield return result[(i + j) % takeCount];
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
List<int> l = new List<int> {4, 6, 3, 6, 2, 5, 7};
List<int> lastElements = l.TakeLast(3).ToList();
Run Code Online (Sandbox Code Playgroud)
它的工作原理是使用大小为N的环形缓冲区来存储它们看到的元素,用新的元素覆盖旧元素.当到达可枚举的末尾时,环形缓冲区包含最后N个元素.
Ray*_*ega 20
.NET Core 2.0提供LINQ方法TakeLast():
https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.takelast
例如:
Enumerable
.Range(1, 10)
.TakeLast(3) // <--- takes last 3 items
.ToList()
.ForEach(i => System.Console.WriteLine(i))
// outputs:
// 8
// 9
// 10
Run Code Online (Sandbox Code Playgroud)
Nic*_*ock 11
我很惊讶没有人提到它,但SkipWhile确实有一个使用元素索引的方法.
public static IEnumerable<T> TakeLastN<T>(this IEnumerable<T> source, int n)
{
if (source == null)
throw new ArgumentNullException("Source cannot be null");
int goldenIndex = source.Count() - n;
return source.SkipWhile((val, index) => index < goldenIndex);
}
//Or if you like them one-liners (in the spirit of the current accepted answer);
//However, this is most likely impractical due to the repeated calculations
collection.SkipWhile((val, index) => index < collection.Count() - N)
Run Code Online (Sandbox Code Playgroud)
此解决方案与其他解决方案相比,唯一可感知的好处是,您可以选择添加谓词以生成更强大,更高效的LINQ查询,而不是两次单独的操作遍历IEnumerable两次.
public static IEnumerable<T> FilterLastN<T>(this IEnumerable<T> source, int n, Predicate<T> pred)
{
int goldenIndex = source.Count() - n;
return source.SkipWhile((val, index) => index < goldenIndex && pred(val));
}
Run Code Online (Sandbox Code Playgroud)
在RX的System.Interactive程序集中使用EnumerableEx.TakeLast.它是像@ Mark's一样的O(N)实现,但它使用队列而不是环形缓冲区构造(并在达到缓冲区容量时将项目出列).
(注意:这是IEnumerable版本 - 不是IObservable版本,尽管两者的实现完全相同)
如果你不介意作为monad的一部分浸入Rx,你可以使用TakeLast:
IEnumerable<int> source = Enumerable.Range(1, 10000);
IEnumerable<int> lastThree = source.AsObservable().TakeLast(3).AsEnumerable();
Run Code Online (Sandbox Code Playgroud)
如果您正在处理带有密钥的集合(例如,来自数据库的条目),那么快速(即比所选答案更快)解决方案将是
collection.OrderByDescending(c => c.Key).Take(3).OrderBy(c => c.Key);
Run Code Online (Sandbox Code Playgroud)