Suhail Mumtaz Awan
Suhail Mumtaz Awan

Reputation: 3483

How to take last records from list

I have list as follows

static List<MessageDetail> CurrentMessage = new List<MessageDetail>();

Dynamically, values assigned to this list for example:

CurrentMessage.Add(new MessageDetail { UserName = 123,GroupName = somegrp, Message = somemsg });

Here, I want to take last 5 or so records.

// this returns first 5 result, dont want to user orderby clause either
CurrentMessagesForGroup = CurrentMessage
                          .Where(c => c.GroupName == groupName)
                          .Take(5).ToList();

Is there a way to implement TakeLast() attribute? Or any kind of help will be appreciated. Thanks for your time.

Upvotes: 1

Views: 6377

Answers (7)

Shuvo Amin
Shuvo Amin

Reputation: 881

If anyone using DotNet Core 2 or above or DotNet Standard 2.1 or above then you can use Linq's built in .TakeLast()

Reference: Microsoft Documentation here

Upvotes: 2

Antonio Gil Espinosa
Antonio Gil Espinosa

Reputation: 41

I like this implementation, it uses a circular buffer.

public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> input, int n)
{
    if (n == 0)
        yield break;

    int tail = 0;
    int head = 0;
    int count = 0;

    T[] buffer = new T[n];

    foreach (T item in input)
    {
        buffer[tail] = item;

        tail = (tail + 1) % n;

        if (count < n)
            count++;
        else
            head = (head + 1) % n;
    }

    for (int i = 0; i < count; i++)
        yield return buffer[(head + i) % n];
}

Upvotes: 1

Salah Akbari
Salah Akbari

Reputation: 39976

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

Upvotes: 3

Jaimie Knox
Jaimie Knox

Reputation: 166

I use an extension method for this.

public static IEnumerable<T> TakeLast<T>(this IEnumerable<T> source, int numElements)
{
    return source.Skip(Math.Max(0, source.Count() - numElements));
}

And to use it:

CurrentMessagesForGroup = CurrentMessage.Where(c => c.GroupName == groupName).TakeLast(5).ToList();

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

Upvotes: 1

Syam Kumar
Syam Kumar

Reputation: 383

If the MessageDetails class has numeric Id or Created date time we can use

var lastRecords= CurrentMessage.OrderByDescending(i=>i.Id).Where(p=>p.GroupName==groupName).Take(5).ToList(); 

Upvotes: 0

J. Steen
J. Steen

Reputation: 15578

You could use Reverse(), which is slightly perverse.

CurrentMessagesForGroup = CurrentMessage
                    .Where(c => c.GroupName == groupName)
                    .Reverse()
                    .Take(5).ToList();

Upvotes: 1

Murray Foxcroft
Murray Foxcroft

Reputation: 13795

Use an OrderBy (ASC or DESC) to get the records lined up correctly for your Take operation.

Ascending:

CurrentMessagesForGroup = CurrentMessage
                        .Where(c => c.GroupName == groupName)
                        .OrderBy(c => c.GroupName)
                        .Take(5)
                        .ToList();

or Descending:

CurrentMessagesForGroup = CurrentMessage
                        .Where(c => c.GroupName == groupName)
                        .OrderByDescending(c => c.GroupName)
                        .Take(5)
                        .ToList();

Upvotes: 3

Related Questions