tags:

views:

1739

answers:

6

Is there a better way to do this?

string[] s = {"zero", "one", "two", "three", "four", "five"};

var x = 
s
.Select((a,i) => new {Value = a, Index = i})
.Where(b => b.Value.StartsWith("t"))
.Select(c => c.Index);

i.e. I'm looking for a more efficient or more elegant way to get the positions of the items matching the criteria.

+2  A: 

Seems fine to me. You might save a couple characters by changing the select to:

.Select((Value, Index) => new {Value, Index})
Mark S. Rasmussen
Thanks - I didn't know you could do that - I thought you had to reassign.
Guy
It's called a "projection initializer" - it basically takes the last subexpression (which must be a field or property) within the expression and uses that for the name. So you could do x.GetFoo().Bar and that would be equivalent to Bar=x.GetFoo().Bar.
Jon Skeet
+2  A: 

If you're just using the example as a way to learn LINQ, ignore this post.


It's not clear to me that LINQ is actually the best way to do this. The code below seems like it would be more efficient since no new anonymous type needs to be created. Granted, your example may be contrived and the technique might be more useful in a different context, for example in a data structure where it could take advantage of an index on value, but the code below is reasonably straight-forward, understandable (no thought required) and arguably more efficient.

string[] s = {"zero", "one", "two", "three", "four", "five"};
List<int> matchingIndices = new List<int>();

for (int i = 0; i < s.Length; ++i) 
{
   if (s[i].StartWith("t"))
   {
      matchingIndices.Add(i);
   }
}
tvanfosson
Thanks for the answer. I agree that this would be more efficient. As you guessed this is a simplified version of something more complex that "has" to be done in LINQ in this particular case.
Guy
And that exact code only works when you're using List instead of an arbitrary IEnumerable. Otherwise you'd have to use foreach and a separate local variable.
Jon Skeet
+7  A: 

You could easily add your own extension method:

public static IEnumerable<int> IndexesWhere<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
    int index=0;
    foreach (T element in source)
    {
        if (predicate(element))
        {
            yield return index;
        }
        index++;
    }
}

Then use it with:

string[] s = {"zero", "one", "two", "three", "four", "five"};
var x = s.IndexesWhere(t => t.StartsWith("t"));
Jon Skeet
A: 

There is also FindIndex method in Collection List for which you create a delete method which can return the index from the collection. you can refer to the following link in msdn http://msdn.microsoft.com/en-us/library/x1xzf2ca.aspx.

A: 

How about this? It's similar to the original poster's but I first select the indexes and then build a collection which matches the criteria.

var x = s.Select((a, i) => i).Where(i => s[i].StartsWith("t"));

This is a tad less efficient than some of the other answers as the list is fully iterated over twice.

Terrence
A: 

Why can't we use List.FindIndexOf, actually linq complicates your solution

Suresh