Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#4.0

LINQ: Introducing the Take Last Operators

4.00/5 (1 vote)
12 Jan 2011CPOL2 min read 15.8K  
LINQ: Introducing the Take Last Operators

Some time ago, I needed to retrieve the last items of a sequence that satisfied some criteria and, looking at the operators available in the Enumerable class, I noticed that there wasn’t such an operator.StatCounter

The only way to achieve this was to reverse the sequence, take the items that satisfied the criteria and reverse the resulting sequence. Something like this:

C#
sequence.Reverse().TakeWhile(criteria).Reverse();

Looks quite simple, right? First, we call the Reverse method to produce a new sequence with the same items as the original sequence but in the reverse order, then we call the TakeWhile method to take the first item that satisfies the criteria and then call the Reverse</strong> method again to restore the original order of the items.

The problem with this approach is that the Reverse method buffers the entire sequence before iterating through its items in the reverse order - and the above code uses it twice. This means iterating over all items in the original sequence and buffer them all, iterating over first items of the resulting sequence that satisfy the criteria and buffer them all and, finally, iterate over that result to produce the final sequence.

If you’re counting, you’ve come to the conclusion that all items in the original sequence will be iterated over once and the ones in the resulting sequence will be iterated again three times. If the original sequence is large, this can take lots of memory and time.

There’s another issue if you’re using the variant that uses the index of the item in the original sequence in the evaluation of the selection criteria (^). When we reverse the order of the items, the indexes will be reversed and the predicate must take that in account, which might not be possible if you don’t know the number of items in the original sequence.

There must be a better way, and that’s why I implemented the Take Last Operators:

NameDescriptionExample
TakeLast<TSource>(IEnumerable<TSource>)

Returns a specified number of contiguous elements from the end of a sequence.

C#
int[] grades = { 59, 82, 70, 56, 92, 98, 85 };

var topThreeGrades = grades
                     .OrderBy(grade => grade)
                     .TakeLast(3);

Console.WriteLine("The top three grades are:");
foreach (int grade in topThreeGrades)
{
    Console.WriteLine(grade);
}
/*
This code produces the following output:

The top three grades are:
98
92
85
*/
TakeLastWhile<TSource>(IEnumerable<TSource>, Func<TSource, Boolean>)

Returns the elements from the end of a sequence as long as the specified condition is true.

C#
string[] fruits =
    {
        "apple",
        "passionfruit",
        "banana",
        "mango",
        "orange",
        "blueberry",
        "grape",
        "strawberry"
    };

var query = fruits
            .TakeLastWhile(fruit => 
        string.Compare("orange", 
        fruit, true) != 0);

foreach (string fruit in query)
{
    Console.WriteLine(fruit);
}

/*
This code produces the following output:

blueberry
grape
strawberry
*/
TakeLastWhile<TSource>(IEnumerable<TSource>, Func<TSource, Int32, Boolean>)

Returns the elements from the end of a sequence as long as the specified condition is true.

C#
string[] fruits =
    {
        "apple",
        "passionfruit",
        "banana",
        "mango",
        "orange",
        "blueberry",
        "grape",
        "strawberry"
    };

var query = fruits
            .TakeLastWhile((fruit, index) => 
        fruit.Length >= index);

foreach (string fruit in query)
{
    Console.WriteLine(fruit);
}

/*
This code produces the following output:

strawberry
*/

You can find these (and more) operators in my CodePlex project for LINQ utilities and operators: PauloMorgado.Linq.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)