Johann Gerell
Johann Gerell

Reputation: 25591

Using LINQ, how do I choose items at particular indexes?

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?

Upvotes: 4

Views: 362

Answers (6)

Ani
Ani

Reputation: 113442

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.

Upvotes: 1

Kinjal
Kinjal

Reputation: 21

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

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1502406

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]);

Upvotes: 11

Adrian Zanescu
Adrian Zanescu

Reputation: 8008

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;

Upvotes: 1

PostMan
PostMan

Reputation: 6967

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);

Upvotes: 2

Elisha
Elisha

Reputation: 23780

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]);

Upvotes: 12

Related Questions