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
?
views:
190answers:
6var 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'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]);
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);
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
.
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;