Est-il possible de commencer à itérer à partir d'un élément autre que le premier en utilisant foreach?

86

Je pense à implémenter IEnumerable pour ma collection personnalisée (un arbre) afin que je puisse utiliser foreach pour parcourir mon arbre. Cependant, pour autant que je sache, foreach commence toujours par le premier élément de la collection. Je voudrais choisir à partir de quel élément pourchaque commence. Est-il possible de changer en quelque sorte l'élément à partir duquel foreach commence?

enfer
la source
1
Je
suivrais
Si vous ne partez pas du premier élément, comment définissez-vous le comportement de l'itérateur? Que se passe-t-il quand il atteint la fin de la séquence? Retourne-t-il ensuite au premier élément et itère sur les éléments restants?
Dan J
Le commentaire de @ ChaosPandion est l'endroit où j'allais avec ces questions. :)
Dan J

Réponses:

162

Oui. Procédez comme suit:

Collection<string> myCollection = new Collection<string>;

foreach (string curString in myCollection.Skip(3))
    //Dostuff

Skipest une fonction IEnumerable qui ignore le nombre que vous spécifiez à partir de l'index actuel. D'un autre côté, si vous vouliez n'utiliser que les trois premiers, vous utiliseriez .Take:

foreach (string curString in myCollection.Take(3))

Ceux-ci peuvent même être jumelés, donc si vous ne vouliez que les 4-6 éléments, vous pourriez faire:

foreach (string curString in myCollection.Skip(3).Take(3))
MoarCodePlz
la source
Bon appel. WTB auto-complete (gahh ne se souvient plus du vrai nom de celui-ci) dans stackoverflow;)
MoarCodePlz
Cela saute-t-il réellement ou crée-t-il simplement une copie? IE ces deux seraient-ils différents: foreach(List<string> row in rows.skip(1)){ row[0] = "newInfo"; } vs for(int i=1; i<rows.Count;i++){ List<string> row = rows[i]; row[0] = "newStuff"; }
TigerBear
25

Il est plus simple d'utiliser la Skipméthode dans LINQ to Objects pour cela, pour ignorer un nombre donné d'éléments:

foreach (var value in sequence.Skip(1)) // Skips just one value
{
    ...
}

Évidemment, changez simplement 1 pour toute autre valeur pour ignorer un nombre différent d'éléments ...

De même, vous pouvez utiliser Takepour limiter le nombre d'éléments qui sont retournés.

Vous pouvez en savoir plus sur ces deux (et les méthodes SkipWhileet TakeWhileméthodes associées ) dans ma série de blogs Edulinq .

Jon Skeet
la source
Jon, cela ne présente-t-il pas le risque d'une exception si "séquence" est Null?
WEFX
@WEFX: Eh bien, nous ne savons pas si cela sequencepeut légitimement l'être nullou non. Vraisemblablement, l'OP peut gérer cela eux-mêmes - cela ne fait pas vraiment partie de la question ...
Jon Skeet
5

Vous pouvez utiliser Enumerable.Skip pour ignorer certains éléments et le faire commencer là.

Par exemple:

foreach(item in theTree.Skip(9))  // Skips the first 9 items
{
    // Do something

Cependant, si vous écrivez un arbre, vous voudrez peut-être fournir un membre sur l'élément d'arborescence lui-même qui renverra un nouveau IEnumerable<T>qui énumérera à partir de là. Cela serait potentiellement plus utile à long terme.

Reed Copsey
la source
Quelque chose comme IEnumerable<T> SubTree(rootIndex)? Bon appel!
Dan J
Merci pour la réponse, skip semble prometteur, en lisant les informations MSDN sur skip, je suis arrivé à la conclusion qu'il est peut-être possible d'écrire une méthode StartFrom (cet arbre IEnumerable <MyTree>, élément MyTreeElement) qui serait une méthode d'extension pour IEnumerable <MyTree> qui retournerait IEnumerable <MyTreeElement> à partir de laquelle je devrais commencer à itérer. Alors peut-être que je pourrais utiliser cette méthode comme foreach (élément var dans tree.StartFrom (someElement))).
inferno
@inferno: Oui, vous pouvez facilement le faire. Cependant, je voudrais simplement ajouter une méthode / propriété à MyTreeElement qui ressemblait plus à:, IEnumerable<MyTreeElement> Childrendonc vous pouvez simplement faire: foreach(var item in someElement.Children)- beaucoup plus clair que l'utilisation des méthodes d'extension IMO.
Reed Copsey
-1

Foreachitérera sur votre collection de la manière définie par votre implémentation de IEnumerable. Ainsi, bien que vous puissiez ignorer des éléments (comme suggéré ci-dessus), vous continuez techniquement à parcourir les éléments dans le même ordre.

Vous ne savez pas ce que vous essayez d'accomplir, mais votre classe peut avoir plusieurs IEnumerablepropriétés, chacune énumérant les éléments dans un ordre spécifique.

Rob
la source
-1

Si vous souhaitez ignorer la lecture des lignes dans un DataGridView, essayez ceci

foreach (DataGridViewRow row in dataGridView1.Rows.Cast<DataGridViewRow().Skip(3))

Si vous souhaitez copier le contenu de l'une DataGridViewvers une autre en sautant des lignes, essayez ceci,

foreach (DataGridViewRow row in dataGridView1.Rows.Cast<DataGridViewRow>().Skip(3))
{
    foreach (DataGridViewCell cell in row.Cells)
    {
        string value = cell.Value.ToString();
        dataGridView2.Rows[i].Cells[j].Value = cell.Value.ToString();
        j++;
    }
    i++;
    j = 0;
}

cela copie le contenu de l'un DataGridViewà l'autre en sautant 3 lignes.

Juned Khan Momin
la source
Cela ne répond pas à la question qui a été posée - la question ne mentionne pas DataGridViewdu tout (ni même un cadre d'interface utilisateur).
NightOwl888
@ NightOwl888 cela a été répondu parce que la question était de sauter certaines itérations et d'itérer après cela dans la boucle foreach. Dans mon exemple, je viens d'utiliser Skip () pour appliquer sur DataGridView.
Juned Khan Momin