tags:

views:

54

answers:

3

I'm trying to get the latest x entries in chronological order. currently I'm doing:

var query = db.OrderByDecending(x => x.date).Take(x).OrderBy(x => x.date)

It seems crazy to be sorting in one direction, limiting and then sorting in the other. Its not like what I'm currently using is causing me sleepless nights, I just wonder whether there is a better way...

+4  A: 

By the second OrderBy call, the list is already sorted, but in the wrong order. Therefore, you can call Reverse, which will be faster.

var query = db.OrderByDescending(x => x.date).Take(x).Reverse();
SLaks
I wish I could 'db.OrderBy(x => x.date).TakeEnd(x)'
Tim
A: 

It depends what 'db' is actually. If its a a collection then SLaks advice stands. On the other hand ff is a SQL database and this is a linq2sql query or a linq to EF then actually the two OrderBy are better. that's what SQL is all about: describe the desired result and let the database engine figure out the optimal access path and query plan to satisfy your request.

Remus Rusanu
db is an IEnumerable wrapper over a flat file data store...I assumed that Linq2sql would be similar but I suppose that wouldn't be that efficient...
Tim
A: 

It requires some sort of stream buffer data structure, but you can quite easily write a TakeEnd method. Though in this specific case using Reverse is probably a better idea.

This would allow you to do something like this:

var query = db.OrderBy(x => x.date).TakeEnd(x);

using this code:

public static class Ext
{
    private class StreamBuffer<T> : IEnumerable<T>
    {
        private int head = 0;
        private bool filled = false;

        private T[] stream;
        public int Size { get; private set; }

        public StreamBuffer(int size)
        {
            Size = size;
            stream = new T[Size];
        }

        public void Add(T item)
        {
            stream[head] = item;

            head += 1;
            if (head >= Size)
            {
                head = 0;
                filled = true;
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            int start = filled ? head : 0;
            int size = filled ? Size : head;
            for (int i = 0; i < size; i += 1)
            {
                int p = (start + i) % Size;
                yield return stream[p];
            }
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public static IEnumerable<T> TakeEnd<T>(this IEnumerable<T> enumerable, int count)
    {
        StreamBuffer<T> buffer = new StreamBuffer<T>(count);
        foreach (T t in enumerable)
        {
            buffer.Add(t);
        }
        return buffer;
    }
}
ICR