views:

190

answers:

6

If I have an IEnumerable<Foo> allFoos and an IEnumerable<Int32> bestFooIndexes, how can I get a new IEnumerable<Foo> bestFoos containing the Foo entries from allFoos at the indexes specified by bestFooIndexes?

+12  A: 
var bestFoos = bestFooIndexes.Select(index => allFoos.ElementAt(index));

If you're worried about performance and the collections are large engouh:

List<Foo> allFoosList = allFoos.ToList();
var bestFoos = bestFooIndexes.Select(index => allFoosList[index]);
Elisha
@Timwi: I was in the middle of writing exactly that suggestion :)
Jon Skeet
+12  A: 

Elisha's answer will certainly work, but it may be very inefficient... it depends on what allFoos is implemented by. If it's an implementation of IList<T>, ElementAt will be efficient - but if it's actually the result of (say) a LINQ to Objects query, then the query will be re-run for every index. So it may be more efficient to write:

var allFoosList = allFoos.ToList();
// Given that we *know* allFoosList is a list, we can just use the indexer
// rather than getting ElementAt to perform the optimization on each iteration
var bestFoos = bestFooIndexes.Select(index => allFoosList[index]);

You could to this only when required, of course:

IList<Foo> allFoosList = allFoos as IList<Foo> ?? allFoos.ToList();
var bestFoos = bestFooIndexes.Select(index => allFoosList[index]);
Jon Skeet
You probably mean `bestFooIndexes.Select(index => allFoosList[index]`
Dan Dumitru
@Dan: Yes, thanks - fixed.
Jon Skeet
+1: I like the "as IList<Foo> ?? allFoos.ToList()" pattern.
Henrik
@Henrik: I only used it myself for the first time the other day :)
Jon Skeet
It doesn't seem fair that Elisha's wasn't given the accepted answer considering he got there first with same answer.
Bear Monkey
@Bear: Elisha's answer was good, but the added sugar in Jon's made it better. I didn't explicitly think of the fact that *... If it's an implementation of IList<T>, ElementAt will be efficient ...* and I also like *as IList<Foo> ?? allFoos.ToList()*.
Johann Gerell
@Johann Ok thanks for answering. Just fighting for the underdog ;)Yes Linq optimisations for ICollection and IList are well done in general. Ill stop polluting you post with inane comments now :)
Bear Monkey
+2  A: 

You could make an extension method like so:

public IEnumerable<T> ElementsAt(this IEnumerable<T> list, IEnumerable<int> indexes)
{
     foreach(var index in indexes)
     {
           yield return list.ElementAt(index);
     }

}

Then you could go something like this

var bestFoos = allFoos.ElementsAt(bestFooIndexes);
PostMan
Why the down vote?
PostMan
@PostMan: Dunno. I like it - it rhymes with what I already use to pick one: `var bestFoo = allFoos.ElementAtOrDefault(bestFooIndex)`
Johann Gerell
+1  A: 

Jon Skeet's / Elisha's answer is the way to go.

Here's a slightly different solution, less efficient in all likelihood:

var bestFooIndices = new HashSet<int>(bestFooIndexes);
var bestFoos = allFoos.Where((foo, index) => bestFooIndices.Contains(index));

Repeats contained in bestFooIndexes will not produce duplicates in the result. Additionally, elements in the result will be ordered by their enumeration order in allFoos rather than by the order in which they are present in bestFooIndexes.

Ani
+1  A: 

Another solution based on join:

var bestFoos = from entry in allFoos
                               .Select((a, i) = new {Index = i, Element = a})
           join index in bestFooIndexed on entry.Index equals index
           select entry.Element;
AZ
A: 

var bestFoosFromAllFoos = allFoos.Where((s) => bestFoos.Contains(s));

Kinjal