Using Linq to Get the Last N Elements of a Collection

Using Linq to get the last N elements of a collection?


collection.Skip(Math.Max(0, collection.Count() - N));

This approach preserves item order without a dependency on any sorting, and has broad compatibility across several LINQ providers.

It is important to take care not to call Skip with a negative number. Some providers, such as the Entity Framework, will produce an ArgumentException when presented with a negative argument. The call to Math.Max avoids this neatly.

The class below has all of the essentials for extension methods, which are: a static class, a static method, and use of the this keyword.

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

A brief note on performance:

Because the call to Count() can cause enumeration of certain data structures, this approach has the risk of causing two passes over the data. This isn't really a problem with most enumerables; in fact, optimizations exist already for Lists, Arrays, and even EF queries to evaluate the Count() operation in O(1) time.

If, however, you must use a forward-only enumerable and would like to avoid making two passes, consider a one-pass algorithm like Lasse V. Karlsen or Mark Byers describe. Both of these approaches use a temporary buffer to hold items while enumerating, which are yielded once the end of the collection is found.

How to take last records from list

Use skip:

CurrentMessagesForGroup = CurrentMessage
.Where(c => c.GroupName == groupName).Skip(Math.Max(0, CurrentMessage.Count() - 5)).ToList();

EDIT: I also find this that I think it is more easier to use (MoreLinq):

using MoreLinq;

var CurrentMessagesForGroup2 = CurrentMessage.TakeLast(5);

How to take all but the last element in a sequence using LINQ?

I don't know a Linq solution - But you can easily code the algorithm by yourself using generators (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()));
}

Or as a generalized solution discarding the last n items (using a queue like suggested in the comments):

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

Get Last N Element From Entity by LINQ

Under assumption that _db.Table1 is queryable you can try following:

var id = _db.table1.Max(e => e.Id);

Get last element (based on names) in a list - LINQ C#

Why not ignore anonymous types and SelectMany and just do:

modList.GroupBy(p => p.Name).Select(g => g.Last())

It's got nothing to do with anonymous types as per your title, but it does result in the sort of results you mention at the start of the question.

how to get specific collection from a collection with linq?

Short answer based on my understanding of this:

var forms = root.Data
.SelectMany(datum => datum.Evaluations)
.Select(evaluation => evaluation.Form)
.ToList();

Long answer with test

You don't specify class Form, hence I tested with this minimum code:

using System.Collections.Generic;

namespace XUnitTestProject.StackOverflow
{
public class Root
{
public List<Datum> Data { get; set; }
}

public class Datum
{
public List<Evaluation> Evaluations { get; set; }
}

public class Form
{
public string Name { get; set; }
}

public class Evaluation
{
public Form Form { get; set; }
}
}

And a test class

using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace XUnitTestProject.StackOverflow
{
public class LinqTests
{
[Fact]
public void Test()
{
var forms = CreateValidRoot().Data
.SelectMany(datum => datum.Evaluations)
.Select(evaluation => evaluation.Form)
.ToList();

Assert.Equal(2, forms.Count);
Assert.Equal("~form1~", forms[0].Name);
Assert.Equal("~form2~", forms[1].Name);
}

private static Root CreateValidRoot() => new()
{
Data = new List<Datum>
{
new()
{
Evaluations = new List<Evaluation>
{
new() {Form = new Form{Name = "~form1~"}}
}
},
new()
{
Evaluations = new List<Evaluation>
{
new() {Form = new Form{Name = "~form2~"}}
}
}
}
};
}
}

Using LINQ expression to get previous element from a collection

You could use TakeWhile and LastOrDefault:

var meeting = Meetings.TakeWhile(m => !m.Selected)
.LastOrDefault();

if (meeting != null)
{
// Use the meeting
}
else
{
// The first meeting was selected, or there are no meetings
}

TakeWhile basically stops when it hits the first item that doesn't match the predicate - it's like a Where that stops early, in other words.

You need to be careful with ordering though - you're pretty much assuming there's a bunch of unselected meetings, followed by selected ones.



Related Topics



Leave a reply



Submit