foreach cu index [duplicate] (Programare, C#, Foreach)

Ken a intrebat.

Există un echivalent în C# al lui Python enumerate() și Ruby’s each_with_index?

Comentarii

  • Dacă folosiți LINQ, există suprascrieri ale diferitelor funcții care permit enumerarea. În caz contrar, de obicei, ești blocat folosind o variabilă pe care o incrementezi singur. –  > Por GWLlosa.
  • Putem actualiza această întrebare la C# 7.0, unde există acum tuple? Mă întreb cum ar arăta o soluție folosind tuple. –  > Por Hendrik Wiese.
  • Nu este aceasta o caracteristică a foreach că puteți procesa fiecare element în mod absolut decuplabil de poziția sa în listă? –  > Por Konstantin A. Magg.
10 răspunsuri
Dan

Păstrez această metodă de extensie pentru asta:

public static void Each<T>(this IEnumerable<T> ie, Action<T, int> action)
{
    var i = 0;
    foreach (var e in ie) action(e, i++);
}

Și o folosesc așa:

var strings = new List<string>();
strings.Each((str, n) =>
{
    // hooray
});

Sau pentru a permite break-un comportament asemănător:

public static bool Each<T>(this IEnumerable<T> ie, Func<T, int, bool> action)
{
    int i = 0;
    foreach (T e in ie) if (!action(e, i++)) return false;
    return true;
}

var strings = new List<string>() { "a", "b", "c" };

bool iteratedAll = strings.Each ((str, n)) =>
{
    if (str == "b") return false;
    return true;
});

Comentarii

    30

  • nu se poate întrerupe în această buclă foreach. –  > Por Tri Q Tran.
  • 27

  • Asta pentru că nu este o buclă foreach. –  > Por Dan.
  • @TriQ Ăsta este un alt motiv pentru a-l prefera, de cele mai multe ori. 🙂 –  > Por Yam Marcovic.
  • @DanFinch Nu am fost sarcastic, căutam în mod legitim un răspuns. –  > Por TankorSmash.
  • @TankorSmash Este o „metodă de extensie”. Căutați asta. Un pic de magie C# minunat. –  > Por Kamil Szot.
JaredPar

Puteți face următoarele

foreach (var it in someCollection.Select((x, i) => new { Value = x, Index = i }) )
{
   if (it.Index > SomeNumber) //      
}

Acest lucru va crea o valoare de tip anonim pentru fiecare intrare din colecție. Aceasta va avea două proprietăți

  • Value: cu valoarea originală din colecție
  • Index: cu indicele din colecție

Comentarii

    18

  • Inteligent, dar este ca și cum v-ați scărpina urechea stângă cu mâna dreaptă. Cred că voi păstra eu însumi indexul, pentru a nu-i deruta pe viitorii mentenanți. –  > Por Ken.
  • 41

  • @Neil, sunt uimit de faptul că oamenii cred că aceasta este o problemă de mentenanță. Această supraîncărcare a Select (și a altor metode LINQ) a fost furnizată cu scopul complet de a face operații ca aceasta. –  > Por JaredPar.
  • Nu văd nicio problemă de întreținere. Dacă un viitor responsabil cu întreținerea este incapabil să acceseze documentația MSDN și să caute supraîncărcările metodei Select, este propria lui problemă. Vă îngrijorează numele variabilelor? Doar faceți-o: (Value, Index) => select new { Value, Index } pentru lambda. –  > Por Joshua Rodgers.
  • @Lodewijk Fără o colecție de susținere, IEnumerable<T>.ElementAt(i) este O(n) (a se vedea Schlemiel pictorul). Și ce se întâmplă dacă aveți un enumerabil leneș care este scump de evaluat și conține un număr mare de înregistrări? Cu o buclă for, trebuie să așteptați pentru IEnumerable<T>.Count() să se întoarcă înainte de a putea începe procesarea înregistrărilor. O buclă for nu este, în general, adecvată pentru utilizarea cu IEnumerable<T>. –  > Por piedar.
  • Acest lucru se poate scurta puțin atunci când se utilizează un ValueTuple C# 7: foreach (var (x, i) in someCollection.Select((x, i) => (x, i)) ) { ... } –  > Por FernAndr.
David Morton

Foreach C# nu are un index încorporat. Va trebui să adăugați un număr întreg în afara buclei foreach și să îl incrementați de fiecare dată.

int i = -1;
foreach (Widget w in widgets)
{
   i++;
   // do something
}

Alternativ, ai putea folosi o buclă for standard, după cum urmează:

for (int i = 0; i < widgets.Length; i++)
{
   w = widgets[i];
   // do something
}

Comentarii

    18

  • Cred că ar trebui să inițializați i la -1 și să îl incrementați la începutul corpului buclei pentru a vă asigura că o instrucțiune „continue” nu cauzează probleme. –  > Por Tamas Czinege.
  • @DrJokepu de ce nu păstrați pur și simplu i = 0 și nu-l incrementați înainte de paranteza de închidere a instrucțiunii foreach? Inițializarea cu ceva != 0 declanșează atenția atunci când parcurgi mai târziu codul… –  > Por Adi.
  • a funcționat în javascript –  > Por Taufik Nurhidayat.
Daniel K

Îmi place să pot folosi foreach, așa că am făcut o metodă de extensie și o structură:

public struct EnumeratedInstance<T>
{
    public long cnt;
    public T item;
}

public static IEnumerable<EnumeratedInstance<T>> Enumerate<T>(this IEnumerable<T> collection)
{
    long counter = 0;
    foreach (var item in collection)
    {
        yield return new EnumeratedInstance<T>
        {
            cnt = counter,
            item = item
        };
        counter++;
    }
}

și un exemplu de utilizare:

foreach (var ii in new string[] { "a", "b", "c" }.Enumerate())
{
    Console.WriteLine(ii.item + ii.cnt);
}

Un lucru plăcut este că, dacă sunteți obișnuit cu sintaxa Python, o puteți folosi în continuare:

foreach (var ii in Enumerate(new string[] { "a", "b", "c" }))

Comentarii

  • Ce parte din ultima parte îți pare a fi sintaxa Python? –  > Por ArtOfWarfare.
  • Scuze pentru necropost. Ți-am modificat funcția astfel încât să arate frumos și mai pythonic în utilizare: pastebin.com/aExvenyY –  > Por KgOfHedgehogs.
Jon Skeet

În afară de răspunsurile LINQ deja date, am o „SmartEnumerable” care vă permite să obțineți indexul și „primul/ultimul”. Este un pic urâtă din punct de vedere al sintaxei, dar s-ar putea să vă fie utilă.

Probabil că putem îmbunătăți inferența de tip folosind o metodă statică într-un tip nongeneric, iar tipizarea implicită va fi și ea de ajutor.

Comentarii

  • Minunat! Aceste mici proprietăți ajutătoare (first/last/index) ar trebui să fie incluse în cadrul standard .net! –  > Por Philip Daubmeier.
  • Este grozav, doar că nu-mi place numele său lung și nedecriptiv –  > Por Arek Bal.
  • Linkul pare să fie mort. Cele mai bune linkuri pe care le pot găsi sunt acest (pentru biblioteca cuprinzătoare) și acest pentru blogul lui Jon care discută despre aceasta. –  > Por Robin Macharg.
  • @RobinMacharg: Am editat link-ul, mulțumesc. –  > Por Jon Skeet.
Chris Ammerman

Soluția mea implică o clasă Pair simplă pe care am creat-o pentru utilitate generală și care, din punct de vedere operațional, este, în esență, aceeași cu clasa framework KeyValuePair. Apoi am creat câteva funcții de extensie pentru IEnumerable numite Ordinate (de la termenul din teoria seturilor „ordinal„).

Aceste funcții vor returna pentru fiecare element un obiect Pair care conține indexul și elementul în sine.

public static IEnumerable<Pair<Int32, X>> Ordinate<X>(this IEnumerable<X> lhs)
{
    return lhs.Ordinate(0);
}

public static IEnumerable<Pair<Int32, X>> Ordinate<X>(this IEnumerable<X> lhs, Int32 initial)
{
    Int32 index = initial - 1;

    return lhs.Select(x => new Pair<Int32, X>(++index, x));
}

Comentarii

  • Rețineți că în prezent există o funcție System.Tuple pentru Pair. –  > Por Asherah.
Darryl Braaten

Nu, nu există.

După cum au arătat și alte persoane, există modalități de a simula comportamentul lui Ruby. Dar este posibil să aveți un tip care implementează IEnumerable care să nu expună un index.

vonWippersnap

Aceasta este colecția dvs.

var values = new[] {6, 2, 8, 45, 9, 3, 0};

Creați un interval de indici pentru această colecție

var indexes = Enumerable.Range(0, values.Length).ToList();

Utilizați intervalul pentru a itera cu indexul

indexes.ForEach(i => values[i] += i);
indexes.ForEach(i => Console.Write("[{0}] = {1}", i, values[i]));

Pz.

Tocmai mi-am dat seama de o soluție interesantă:

public class DepthAware<T> : IEnumerable<T>
{
    private readonly IEnumerable<T> source;

    public DepthAware(IEnumerable<T> source)
    {
        this.source = source;
        this.Depth = 0;
    }

    public int Depth { get; private set; }

    private IEnumerable<T> GetItems()
    {
        foreach (var item in source)
        {
            yield return item;
            ++this.Depth;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return GetItems().GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

// Generic type leverage and extension invoking
public static class DepthAware
{
    public static DepthAware<T> AsDepthAware<T>(this IEnumerable<T> source)
    {
        return new DepthAware<T>(source);
    }

    public static DepthAware<T> New<T>(IEnumerable<T> source)
    {
        return new DepthAware<T>(source);
    }
}

Utilizare:

var chars = new[] {'a', 'b', 'c', 'd', 'e', 'f', 'g'}.AsDepthAware();

foreach (var item in chars)
{
    Console.WriteLine("Char: {0}, depth: {1}", item, chars.Depth);
}

Comentarii

  • Resuscitarea unui zombi, deoarece tocmai aveam nevoie de acest lucru: acest lucru se va comporta confuz dacă enumerabilul este iterat de mai multe ori. –  > Por millimoose.
  • Aveți dreptate. Dacă ar fi să implementez acest lucru astăzi, cu siguranță aș merge cu răspunsul acceptat. –  > Por Pz..
VBNight

Depinde de clasa pe care o folosiți.

Dicționar<(Of <(TKey, TValue>)>) Clasa de exemplu susține acest lucru

Clasa generică Dictionary<(Of <(TKey, TValue>)>) oferă o corespondență de la un set de chei la un set de valori.

În scopul enumerării, fiecare element din dicționar este tratat ca o structură KeyValuePair<(Of <(TKey, TValue>)>) reprezentând o valoare și cheia sa. Ordinea în care sunt returnate elementele este nedefinită.

foreach (KeyValuePair kvp in myDictionary) {…}

Tags:,