2010-09-23 6 views
4

Si j'ai un IEnumerable<Foo> allFoos et un IEnumerable<Int32> bestFooIndexes, comment puis-je obtenir un nouveau IEnumerable<Foo> bestFoos contenant les Foo entrées de allFoos aux indices indiqués par bestFooIndexes?En utilisant LINQ, comment choisir des éléments à des index particuliers?

+0

Héhé, regardez combien d'attention vous avez;) –

+0

Si allFoos est indexable alors pourquoi ne pas utiliser IList au lieu de IEnumerable et éviter les conversions/conversions requises dans les exemples ci-dessous? –

+0

@ user373743 Parce qu'il a écrit qu'il utilise un IEnumerable. –

Répondre

11

réponse d'Élisée va certainement travailler, mais il peut être très inefficace ... cela dépend de ce allFoos est mis en œuvre par. Si c'est une implémentation de IList<T>, ElementAt sera efficace - mais si c'est en fait le résultat de (par exemple) une requête LINQ to Objects, alors la requête sera relancée pour chaque index. Donc, il peut être plus efficace d'écrire:

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

Vous pourriez à ce que lorsque cela est nécessaire, bien sûr:

IList<Foo> allFoosList = allFoos as IList<Foo> ?? allFoos.ToList(); 
var bestFoos = bestFooIndexes.Select(index => allFoosList[index]); 
+0

Vous voulez probablement dire 'bestFooIndexes.Select (index => allFoosList [index]' –

+0

@Dan: Oui, merci - fixe –

+0

+1: J'aime le "comme IList ?? allFoos.ToList()" modèle – Henrik

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

Si vous êtes inquiet sur les performances et les collections sont grandes engouh:

List<Foo> allFoosList = allFoos.ToList(); 
var bestFoos = bestFooIndexes.Select(index => allFoosList[index]); 
+0

@Timwi: J'étais au milieu de l'écriture exactement cette suggestion :) –

2

Vous pouvez faire une méthode d'extension comme ceci:

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

} 

Ensuite, vous pouvez aller quelque chose comme ça

var bestFoos = allFoos.ElementsAt(bestFooIndexes); 
+0

Pourquoi le vote vers le bas? – PostMan

+0

Dunno. Je l'aime - ça rime avec ce que j'utilise déjà pour en choisir un: 'var bestFoo = allFoos.ElementAtOrDefault (bestFooIndex)' –

1

La réponse de Jon Skeet/Elisha est la voie à suivre.

est ici une solution légèrement différente, moins efficace, selon toute vraisemblance:

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

Répète contenues dans bestFooIndexes ne produira pas de doublons dans le résultat. De plus, les éléments du résultat seront classés par leur ordre d'énumération au allFoos plutôt que selon l'ordre dans lequel ils sont présents dans bestFooIndexes.

1

Une autre solution basée sur rejoindre:

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

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

Questions connexes