É possível iniciar a iteração de um elemento diferente do primeiro usando foreach?

86

Estou pensando em implementar IEnumerable para minha coleção personalizada (uma árvore) para que eu possa usar foreach para cruzar minha árvore. No entanto, pelo que eu sei, foreach sempre começa a partir do primeiro elemento da coleção. Eu gostaria de escolher qual elemento foreach começa. É possível alterar de alguma forma o elemento a partir do qual o foreach começa?

inferno
fonte
1
Eu seguiria o princípio do menor espanto: en.wikipedia.org/wiki/Principle_of_least_astonishment
ChaosPandion
Se você não começar a partir do primeiro elemento, como definirá o comportamento do iterador? O que acontece quando chega ao final da sequência? Em seguida, ele volta para o primeiro elemento e itera sobre os elementos restantes?
Dan J de
O comentário de @ChaosPandion é onde eu queria chegar com essas perguntas. :)
Dan J

Respostas:

162

Sim. Faça o seguinte:

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

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

Skipé uma função IEnumerable que ignora quantas funções você especificar a partir do índice atual. Por outro lado, se você quiser usar apenas os três primeiros, deve usar .Take:

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

Eles podem até mesmo ser emparelhados, então se você quisesse apenas os 4 a 6 itens, você poderia fazer:

foreach (string curString in myCollection.Skip(3).Take(3))
MoarCodePlz
fonte
Boa decisão. Autocompletar WTB (gahh não me lembro o nome real dele) em stackoverflow;)
MoarCodePlz
Isso realmente pula ou apenas cria uma cópia? foreach(List<string> row in rows.skip(1)){ row[0] = "newInfo"; }for(int i=1; i<rows.Count;i++){ List<string> row = rows[i]; row[0] = "newStuff"; }
TigerBear
25

É mais fácil usar o Skipmétodo em LINQ to Objects para isso, para pular um determinado número de elementos:

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

Obviamente, basta alterar 1 para qualquer outro valor para pular um número diferente de elementos ...

Da mesma forma, você pode usar Takepara limitar o número de elementos que são retornados.

Você pode ler mais sobre ambos (e os métodos SkipWhilee TakeWhilemétodos relacionados ) em minha série de blogs Edulinq .

Jon Skeet
fonte
Jon, isso não corre o risco de uma exceção se a "sequência" for Nula?
WEFX de
@WEFX: Bem, não temos ideia se sequencepode ser legitimamente nullou não. Presumivelmente, o OP pode cuidar disso sozinho - não é realmente parte da questão ...
Jon Skeet
5

Você pode usar Enumerable.Skip para pular alguns elementos e fazer com que comece aí.

Por exemplo:

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

No entanto, se você estiver escrevendo uma árvore, você pode querer fornecer um membro no próprio item da árvore que retornará um novo IEnumerable<T>que será enumerado a partir daí. Isso seria, potencialmente, mais útil no longo prazo.

Reed Copsey
fonte
Algo assim IEnumerable<T> SubTree(rootIndex)? Boa decisão!
Dan J de
Obrigado pela resposta, pular parece promissor, lendo informações do MSDN sobre pular cheguei à conclusão de que talvez seja possível escrever um método StartFrom (esta árvore IEnumerable <MyTree>, elemento MyTreeElement) que seria um método de extensão para IEnumerable <MyTree> que retornaria IEnumerable <MyTreeElement> do qual devo começar a iterar. Então, talvez eu pudesse usar esse método como foreach (var element in tree.StartFrom (someElement))).
inferno
@inferno: Sim, você poderia fazer isso facilmente. No entanto, eu apenas adicionaria um método / propriedade a MyTreeElement que fosse mais parecido com:, IEnumerable<MyTreeElement> Childrenentão você poderia apenas fazer: foreach(var item in someElement.Children)- muito mais claro do que usar métodos de extensão IMO.
Reed Copsey
-1

Foreachirá iterar em sua coleção da maneira definida por sua implementação de IEnumerable. Portanto, embora você possa pular elementos (como sugerido acima), você ainda está tecnicamente iterando sobre os elementos na mesma ordem.

Não tenho certeza do que você está tentando alcançar, mas sua classe pode ter várias IEnumerablepropriedades, cada uma das quais enumera os elementos em uma ordem específica.

Roubar
fonte
-1

Se você quiser pular a leitura das linhas em a DataGridView, tente este

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

Se você quiser copiar o conteúdo de um DataGridViewpara outro pulando linhas, tente isto,

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

isso copia o conteúdo de um DataGridViewpara outro, saltando 3 linhas.

Juned Khan Momin
fonte
Isso não responde à pergunta que foi feita - a pergunta não menciona DataGridViewnada (ou mesmo uma estrutura de IU).
NightOwl888,
@ NightOwl888 isso foi respondido porque a questão era sobre pular algumas iterações e iterar depois disso no loop foreach. No meu exemplo, usei apenas Skip () para aplicar no DataGridView.
Juned Khan Momin