views:

397

answers:

4

I have a IQueryable which is ordered by some condition. Now I want to know the position of a particular element in that IQueryable. Is there a linq expression to get that. Say for example there are 10 elements in the IQueryable and the 6th element matches a condition, I want to get the number 6.

+2  A: 

You could use something like query.TakeWhile(x => !matchesCondition(x)).Count(), though that has the effect of enumerating the preceding values, which may not be what you want.

camccann
You have to enumerate through those preceding values anyway, haven't you?
treaschf
Interesting solution. It would be OK to enumerate the preceding values if it were a memory object, but not for a DB. Note that if the element is not in the list, this function returns the length of the list. Normally you would expect such functions to return -1 or perhaps null.
Mark Byers
@Mark Byers: I read the question's phrasing as implying that the desired element was known to be present, and only the position was desired; and the DB scenario was indeed my concern about enumerating.In practice I suspect that doing this to an IQueryable is unwise, but as a quick hack this approach is at least succinct and understandable.
camccann
+5  A: 

First select each item with its index, then filter the items, and finally extract the original index:

var result = orderedList
    .Select((x, i) => new { Item = x, Index = i })
    .Where(itemWithIndex => itemWithIndex.Item.StartsWith("g"))
    .FirstOrDefault();

int index= -1;
if (result != null)
    index = result.Index;

Test bed:

class Program
{
    static void Main(string[] args)
    {
        var orderedList = new List<string>
        {
            "foo", "bar", "baz", "qux", "quux",
            "corge", "grault", "garply", "waldo",
            "fred", "plugh", "xyzzy", "thud"
        }.OrderBy(x => x);

        // bar, baz, corge, foo, fred, garply, grault,
        // plugh, quux, qux, thud, waldo, xyzzy
        // Find the index of the first element beginning with 'g'.

        var result = orderedList
            .Select((x, i) => new { Item = x, Index = i })
            .Where(itemWithIndex => itemWithIndex.Item.StartsWith("g"))
            .FirstOrDefault();

        int index= -1;
        if (result != null)
            index = result.Index;

        Console.WriteLine("Index: " + index);
    }
}

Output:

Index: 5
Mark Byers
+1- I did it the exact same way in my answer.
RichardOD
A: 
        var query = new List<string>{ "a", "b", "c" };
        int x = 0;
        var index = (from item in query
                     let dummy = ++x
                     where item == "b"
                     select x).FirstOrDefault() - 1;
        if (index == -1)
            Console.WriteLine("Not found");
        else
            Console.WriteLine(index);
Hasan Khan
A: 

You can also use the verson of the "Where" function that incldes the collection index as a parameter to the predicate function. See MSDN for more info.

var result = Enumerable.Range(0, 10).Where((x, i) => i == 6);

The version could result in an empty list if there isn't a 6th element. Also this doesn't evaluate the where clause until you iterate over the result.

Jason