Bestimmen Sie für jede Schleife, welche die letzte Iteration der Schleife ist

233

Ich habe eine foreachSchleife und muss eine Logik ausführen, wenn das letzte Element aus dem ausgewählt wird List, z.

 foreach (Item result in Model.Results)
 {
      //if current result is the last item in Model.Results
      //then do something in the code
 }

Kann ich wissen, welche Schleife die letzte ist, ohne for-Schleife und Zähler zu verwenden?

Missgeschick
quelle
1
Schauen Sie sich meine Antwort hier an, um eine Lösung für eine verwandte Frage zu finden.
Brian Gideon

Antworten:

293

Wenn Sie nur etwas mit dem letzten Element tun müssen (im Gegensatz zu etwas anderem mit dem letzten Element, hilft die Verwendung von LINQ hier:

Item last = Model.Results.Last();
// do something with last

Wenn Sie mit dem letzten Element etwas anderes machen müssen, brauchen Sie etwas wie:

Item last = Model.Results.Last();
foreach (Item result in Model.Results)
{
    // do something with each item
    if (result.Equals(last))
    {
        // do something different with the last item
    }
    else
    {
        // do something different with every item but the last
    }
}

Obwohl Sie wahrscheinlich einen benutzerdefinierten Vergleich schreiben müssen, um sicherzustellen, dass Sie feststellen können, dass der Artikel mit dem von zurückgegebenen Artikel identisch ist Last().

Dieser Ansatz sollte mit Vorsicht angewendet werden, da er Lastmöglicherweise die Sammlung durchlaufen muss. Während dies für kleine Sammlungen möglicherweise kein Problem darstellt, kann es Auswirkungen auf die Leistung haben, wenn es groß wird. Es schlägt auch fehl, wenn die Liste doppelte Elemente enthält. In diesen Fällen kann so etwas angemessener sein:

int totalCount = result.Count();
for (int count = 0; count < totalCount; count++)
{
    Item result = Model.Results[count];

    // do something with each item
    if ((count + 1) == totalCount)
    {
        // do something different with the last item
    }
    else
    {
        // do something different with every item but the last
    }
}
ChrisF
quelle
1
Was ich brauchte war: Wenn die Schleife ihr letztes Element durchläuft: foreach (Element Ergebnis in Model.Results) {if (Ergebnis == Model.Results.Last ()) {<div> last </ div>; } Scheint, dass du so ziemlich dasselbe gemeint hast.
Missgeschick
10
Ihr Code wird zweimal durch die gesamte Sammlung iteriert - schlecht, wenn die Sammlung nicht klein ist. Siehe diese Antwort.
Shimmy Weitzhandler
54
Dies funktioniert nicht wirklich, wenn Ihre Sammlung Duplikate enthält. Wenn Sie beispielsweise mit einer Sammlung von Zeichenfolgen arbeiten und Duplikate vorhanden sind, wird dieser Code "Mit dem letzten Element unterschiedlich" für jedes Vorkommen des letzten Elements in der Liste ausgeführt.
Muttley91
7
Diese Antwort ist alt, aber für andere, die sich diese Antwort ansehen, können Sie das letzte Element abrufen und sicherstellen, dass Sie die Elemente nicht durchlaufen müssen, indem Sie Folgendes verwenden: Item last = Model.Results [Model.Results.Count - 1] Die Anzahl Die Eigenschaft einer Liste erfordert keine Schleife. Wenn Ihre Liste Duplikate enthält, verwenden Sie einfach eine Iteratorvariable in einer for-Schleife. Normal alt für Schleifen sind nicht schlecht.
Michael Harris
Ich schlage vor, var last = Model.Result[Model.Result.Count - 1];schneller als zu verwendenLast()
Tân
184

Wie wäre es mit einer guten, altmodischen Schleife?

for (int i = 0; i < Model.Results.Count; i++) {

     if (i == Model.Results.Count - 1) {
           // this is the last item
     }
}

Oder mit Linq und foreach:

foreach (Item result in Model.Results)   
{   
     if (Model.Results.IndexOf(result) == Model.Results.Count - 1) {
             // this is the last item
     }
}
Fiona - myaccessible.website
quelle
14
So viele Leute überdenken ein einfaches Problem wie dieses, wenn die for-Schleife es bereits perfekt kann. : \
Andrew Hoffman
Die Linq-Lösung ist mein absoluter Favorit! Vielen Dank für das Teilen
Mecograph
Dies ist die angemessenere Antwort als die akzeptierte.
Ratul
Hinweis für alle, die die LINQ-Lösung für eine Sammlung von Zeichenfolgen (oder Werttypen) verwenden möchten: Dies funktioniert im Allgemeinen nicht, da der Vergleich == fehlschlägt, wenn die letzte Zeichenfolge in der Liste auch früher in der Liste angezeigt wird. Es würde nur funktionieren, wenn Sie mit einer Liste arbeiten, die garantiert keine doppelten Zeichenfolgen enthält.
Tawab Wakil
Leider können Sie diese clevere Lösung nicht verwenden, wenn Model.Resultses sich um eine handelt IEnumerable. Sie können Count()vor der Schleife aufrufen , dies kann jedoch zur vollständigen Iteration der Sequenz führen.
Luca Cremonesi
42

Bei Verwendung Last()bestimmter Typen wird die gesamte Sammlung durchlaufen!
Das heißt, wenn Sie einen foreachAnruf tätigen und einen Anruf tätigen Last(), haben Sie zweimal eine Schleife durchgeführt ! Das möchten Sie sicher in großen Sammlungen vermeiden.

Dann besteht die Lösung darin, eine do whileSchleife zu verwenden:

using var enumerator = collection.GetEnumerator();

var last = !enumerator.MoveNext();
T current;

while (!last)
{
  current = enumerator.Current;        

  //process item

  last = !enumerator.MoveNext();        
  if(last)
  {
    //additional processing for last item
  }
}

Wenn der Auflistungstyp nicht vom Typ ist , durchläuft IList<T>die Last()Funktion alle Auflistungselemente.

Prüfung

Wenn Ihre Sammlung wahlfreien Zugriff bietet (z. B. Geräte) IList<T> ), können Sie Ihren Artikel auch wie folgt überprüfen.

if(collection is IList<T> list)
  return collection[^1]; //replace with collection.Count -1 in pre-C#8 apps
Shimmy Weitzhandler
quelle
1
Sind Sie sicher, dass der Enumerator eine usingAnweisung benötigt? Ich dachte, das wird nur benötigt, wenn ein Objekt Betriebssystemressourcen verwaltet, aber nicht für verwaltete Datenstrukturen.
Hockendes Kätzchen
IEnumerator implementiert IDisposable nicht, daher löst die Zeile mit using einen Fehler bei der Kompilierungszeit aus! +1 für die Lösung, meistens können wir nicht einfach ein for anstelle von foreach verwenden, da unzählige Sammlungselemente zur Laufzeit berechnet werden oder die Sequenz keinen Direktzugriff unterstützt.
Saleh
1
Das Generische tut es.
Shimmy Weitzhandler
40

Wie Chris zeigt, wird Linq funktionieren; Verwenden Sie einfach Last (), um eine Referenz auf die letzte in der Aufzählung zu erhalten. Solange Sie nicht mit dieser Referenz arbeiten, führen Sie Ihren normalen Code aus. Wenn Sie jedoch mit dieser Referenz arbeiten, tun Sie Ihre zusätzliche Aufgabe. Sein Nachteil ist, dass es immer O (N) -Komplexität sein wird.

Sie können stattdessen Count () verwenden (dies ist O (1), wenn IEnumerable auch eine ICollection ist; dies gilt für die meisten gängigen integrierten IEnumerables) und Ihren foreach mit einem Zähler kombinieren:

var i=0;
var count = Model.Results.Count();
foreach (Item result in Model.Results)
{
    if (++i == count) //this is the last item
}
KeithS
quelle
22
foreach (var item in objList)
{
  if(objList.LastOrDefault().Equals(item))
  {

  }
}
Gabriel Tiburcio
quelle
Hallo, dies ist der bisher beste Ansatz! Einfach und auf den Punkt. Ein Programmierer-Ding-Ansatz. Warum wählen wir nicht und geben diesem +1 immer mehr!
Hanny Setiawan
1
Das letzte Element sollte vor dem Blockieren nur einmal gefunden werden ( Memoisierung fördern ) foreach. So : var lastItem = objList.LastOrDeafault();. Von der Innenseite der foreachSchleife aus können Sie dies folgendermaßen überprüfen : f (item.Equals(lastItem)) { ... }. In Ihrer ursprünglichen Antwort objList.LastOrDefault()würde das bei jeder "foreach" -Iteration über die Sammlung iterieren ( Polinomialkomplexität ist beteiligt ).
AlexMelw
Falsche Antwort. n ^ 2 Komplexität statt n.
Shimmy Weitzhandler
11

Wie Shimmy hervorgehoben hat, kann die Verwendung von Last () ein Leistungsproblem sein, beispielsweise wenn Ihre Sammlung das Live-Ergebnis eines LINQ-Ausdrucks ist. Um mehrere Iterationen zu verhindern, können Sie eine "ForEach" -Erweiterungsmethode wie die folgende verwenden:

var elements = new[] { "A", "B", "C" };
elements.ForEach((element, info) => {
    if (!info.IsLast) {
        Console.WriteLine(element);
    } else {
        Console.WriteLine("Last one: " + element);
    }
});

Die Erweiterungsmethode sieht folgendermaßen aus (als zusätzlichen Bonus wird Ihnen auch der Index angezeigt und wenn Sie sich das erste Element ansehen):

public static class EnumerableExtensions {
    public delegate void ElementAction<in T>(T element, ElementInfo info);

    public static void ForEach<T>(this IEnumerable<T> elements, ElementAction<T> action) {
        using (IEnumerator<T> enumerator = elements.GetEnumerator())
        {
            bool isFirst = true;
            bool hasNext = enumerator.MoveNext();
            int index = 0;
            while (hasNext)
            {
                T current = enumerator.Current;
                hasNext = enumerator.MoveNext();
                action(current, new ElementInfo(index, isFirst, !hasNext));
                isFirst = false;
                index++;
            }
        }
    }

    public struct ElementInfo {
        public ElementInfo(int index, bool isFirst, bool isLast)
            : this() {
            Index = index;
            IsFirst = isFirst;
            IsLast = isLast;
        }

        public int Index { get; private set; }
        public bool IsFirst { get; private set; }
        public bool IsLast { get; private set; }
    }
}
Daniel Wolf
quelle
9

Wenn Sie die Antwort von Daniel Wolf noch weiter verbessern , können Sie sie auf eine andere stapeln IEnumerable, um mehrere Iterationen und Lambdas zu vermeiden, wie z.

var elements = new[] { "A", "B", "C" };
foreach (var e in elements.Detailed())
{
    if (!e.IsLast) {
        Console.WriteLine(e.Value);
    } else {
        Console.WriteLine("Last one: " + e.Value);
    }
}

Die Implementierung der Erweiterungsmethode:

public static class EnumerableExtensions {
    public static IEnumerable<IterationElement<T>> Detailed<T>(this IEnumerable<T> source)
    {
        if (source == null)
            throw new ArgumentNullException(nameof(source));

        using (var enumerator = source.GetEnumerator())
        {
            bool isFirst = true;
            bool hasNext = enumerator.MoveNext();
            int index = 0;
            while (hasNext)
            {
                T current = enumerator.Current;
                hasNext = enumerator.MoveNext();
                yield return new IterationElement<T>(index, current, isFirst, !hasNext);
                isFirst = false;
                index++;
            }
        }
    }

    public struct IterationElement<T>
    {
        public int Index { get; }
        public bool IsFirst { get; }
        public bool IsLast { get; }
        public T Value { get; }

        public IterationElement(int index, T value, bool isFirst, bool isLast)
        {
            Index = index;
            IsFirst = isFirst;
            IsLast = isLast;
            Value = value;
        }
    }
}
Fabricio Godoy
quelle
1
Die andere Antwort wiederholt die Quelle nicht mehrmals, daher ist dies kein Problem, das Sie beheben. Sie haben in der Tat die Verwendung von erlaubt foreach, was eine Verbesserung ist.
Servy
1
@Servy Ich meine das. Neben einer einzelnen Iteration von der ursprünglichen Antwort vermeide ich Lambdas.
Fabricio Godoy
7

Die Iterator-Implementierung bietet dies nicht. Ihre Sammlung ist möglicherweise IListüber einen Index in O (1) zugänglich. In diesem Fall können Sie eine normale forSchleife verwenden:

for(int i = 0; i < Model.Results.Count; i++)
{
  if(i == Model.Results.Count - 1) doMagic();
}

Wenn Sie die Anzahl kennen, aber nicht über Indizes zugreifen können (Ergebnis ist also ein ICollection), können Sie sich selbst zählen, indem Sie ein iim foreachKörper des Inkrements erhöhen und es mit der Länge vergleichen.

Das alles ist nicht perfekt elegant. Chris 'Lösung ist vielleicht die schönste, die ich bisher gesehen habe.

Matthias Meid
quelle
Beim Vergleich der Leistung Ihres Zählers innerhalb der foreach-Idee mit der Lösung von Chris frage ich mich, was mehr kosten würde - ein einzelner Last () -Aufruf oder die Summe aller hinzugefügten Inkrementierungsoperationen. Ich vermute, es wäre nah.
TTT
6

Was ist mit einem etwas einfacheren Ansatz?

Item last = null;
foreach (Item result in Model.Results)
{
    // do something with each item

    last = result;
}

//Here Item 'last' contains the last object that came in the last of foreach loop.
DoSomethingOnLastElement(last);
faisal
quelle
2
Ich weiß nicht, warum dich jemand abgelehnt hat. Dies ist durchaus akzeptabel, wenn man bedenkt, dass Sie bereits eine Foreach durchführen und die Kosten für o (n) verursachen.
Arviman
2
Trotz der Tatsache, dass die Antwort perfekt ist, um das letzte Element herauszufinden, entspricht sie nicht der Bedingung des OP " ..., bestimmen Sie, welche die letzte Iteration der Schleife ist ". Sie können also nicht feststellen, dass die letzte Iteration tatsächlich die letzte ist, und können daher nicht anders damit umgehen oder sie sogar ignorieren. Das ist der Grund, warum dich jemand herabgestimmt hat. @arviman du warst so neugierig darauf.
AlexMelw
1
Du hast recht, ich habe es total verpasst @ Andrey-WD. Ich denke, die Lösung, die behoben werden muss, besteht darin, "last" einmal vor der Schleife aufzurufen (kann es nicht innerhalb der Schleife tun, da es O (N ^ 2) wäre, und dann prüfen, ob die Referenz damit übereinstimmt.
arviman
5

Der beste Ansatz wäre wahrscheinlich, nur diesen Schritt nach der Schleife auszuführen: z

foreach(Item result in Model.Results)
{
   //loop logic
}

//Post execution logic

Oder wenn Sie etwas für das letzte Ergebnis tun müssen

foreach(Item result in Model.Results)
{
   //loop logic
}

Item lastItem = Model.Results[Model.Results.Count - 1];

//Execute logic on lastItem here
Dustin Hodges
quelle
3

Die akzeptierte Antwort funktioniert nicht für Duplikate in der Sammlung. Wenn Sie auf eingestellt sind foreach, können Sie einfach Ihre eigenen Indexvariablen hinzufügen.

int last = Model.Results.Count - 1;
int index = 0;
foreach (Item result in Model.Results)
{
    //Do Things

    if (index == last)
        //Do Things with the last result

    index++;
}
Ehryk
quelle
2

mit Linq und dem foreach:

foreach (Item result in Model.Results)   
{   
     if (Model.Results.IndexOf(result) == Model.Results.Count - 1) {
             // this is the last item
     }
}

https://code.i-harness.com/de/q/7213ce

HanMyintTun
quelle
1
Dies funktioniert nur, wenn Liste / Sammlung eindeutige Werte hat.
Melleck
1

".Last ()" hat bei mir nicht funktioniert, also musste ich so etwas machen:

Dictionary<string, string> iterativeDictionary = someOtherDictionary;
var index = 0;
iterativeDictionary.ForEach(kvp => 
    index++ == iterativeDictionary.Count ? 
        /*it's the last item */ :
        /*it's not the last item */
);
itcropper
quelle
1

Wenn Sie einige kleine Anpassungen am hervorragenden Code von Jon Skeet vornehmen, können Sie ihn sogar intelligenter gestalten, indem Sie den Zugriff auf das vorherige und das nächste Element zulassen. Dies bedeutet natürlich, dass Sie 1 Punkt in der Implementierung vorlesen müssen. Aus Leistungsgründen werden das vorherige und das nächste Element nur für das aktuelle Iterationselement beibehalten. Es geht so:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
// Based on source: http://jonskeet.uk/csharp/miscutil/

namespace Generic.Utilities
{
    /// <summary>
    /// Static class to make creation easier. If possible though, use the extension
    /// method in SmartEnumerableExt.
    /// </summary>
    public static class SmartEnumerable
    {
        /// <summary>
        /// Extension method to make life easier.
        /// </summary>
        /// <typeparam name="T">Type of enumerable</typeparam>
        /// <param name="source">Source enumerable</param>
        /// <returns>A new SmartEnumerable of the appropriate type</returns>
        public static SmartEnumerable<T> Create<T>(IEnumerable<T> source)
        {
            return new SmartEnumerable<T>(source);
        }
    }

    /// <summary>
    /// Type chaining an IEnumerable&lt;T&gt; to allow the iterating code
    /// to detect the first and last entries simply.
    /// </summary>
    /// <typeparam name="T">Type to iterate over</typeparam>
    public class SmartEnumerable<T> : IEnumerable<SmartEnumerable<T>.Entry>
    {

        /// <summary>
        /// Enumerable we proxy to
        /// </summary>
        readonly IEnumerable<T> enumerable;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="enumerable">Collection to enumerate. Must not be null.</param>
        public SmartEnumerable(IEnumerable<T> enumerable)
        {
            if (enumerable == null)
            {
                throw new ArgumentNullException("enumerable");
            }
            this.enumerable = enumerable;
        }

        /// <summary>
        /// Returns an enumeration of Entry objects, each of which knows
        /// whether it is the first/last of the enumeration, as well as the
        /// current value and next/previous values.
        /// </summary>
        public IEnumerator<Entry> GetEnumerator()
        {
            using (IEnumerator<T> enumerator = enumerable.GetEnumerator())
            {
                if (!enumerator.MoveNext())
                {
                    yield break;
                }
                bool isFirst = true;
                bool isLast = false;
                int index = 0;
                Entry previous = null;

                T current = enumerator.Current;
                isLast = !enumerator.MoveNext();
                var entry = new Entry(isFirst, isLast, current, index++, previous);                
                isFirst = false;
                previous = entry;

                while (!isLast)
                {
                    T next = enumerator.Current;
                    isLast = !enumerator.MoveNext();
                    var entry2 = new Entry(isFirst, isLast, next, index++, entry);
                    entry.SetNext(entry2);
                    yield return entry;

                    previous.UnsetLinks();
                    previous = entry;
                    entry = entry2;                    
                }

                yield return entry;
                previous.UnsetLinks();
            }
        }

        /// <summary>
        /// Non-generic form of GetEnumerator.
        /// </summary>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Represents each entry returned within a collection,
        /// containing the value and whether it is the first and/or
        /// the last entry in the collection's. enumeration
        /// </summary>
        public class Entry
        {
            #region Fields
            private readonly bool isFirst;
            private readonly bool isLast;
            private readonly T value;
            private readonly int index;
            private Entry previous;
            private Entry next = null;
            #endregion

            #region Properties
            /// <summary>
            /// The value of the entry.
            /// </summary>
            public T Value { get { return value; } }

            /// <summary>
            /// Whether or not this entry is first in the collection's enumeration.
            /// </summary>
            public bool IsFirst { get { return isFirst; } }

            /// <summary>
            /// Whether or not this entry is last in the collection's enumeration.
            /// </summary>
            public bool IsLast { get { return isLast; } }

            /// <summary>
            /// The 0-based index of this entry (i.e. how many entries have been returned before this one)
            /// </summary>
            public int Index { get { return index; } }

            /// <summary>
            /// Returns the previous entry.
            /// Only available for the CURRENT entry!
            /// </summary>
            public Entry Previous { get { return previous; } }

            /// <summary>
            /// Returns the next entry for the current iterator.
            /// Only available for the CURRENT entry!
            /// </summary>
            public Entry Next { get { return next; } }
            #endregion

            #region Constructors
            internal Entry(bool isFirst, bool isLast, T value, int index, Entry previous)
            {
                this.isFirst = isFirst;
                this.isLast = isLast;
                this.value = value;
                this.index = index;
                this.previous = previous;
            }
            #endregion

            #region Methods
            /// <summary>
            /// Fix the link to the next item of the IEnumerable
            /// </summary>
            /// <param name="entry"></param>
            internal void SetNext(Entry entry)
            {
                next = entry;
            }

            /// <summary>
            /// Allow previous and next Entry to be garbage collected by setting them to null
            /// </summary>
            internal void UnsetLinks()
            {
                previous = null;
                next = null;
            }

            /// <summary>
            /// Returns "(index)value"
            /// </summary>
            /// <returns></returns>
            public override string ToString()
            {
                return String.Format("({0}){1}", Index, Value);
            }
            #endregion

        }
    }
}
Edwin
quelle
1

So konvertieren Sie foreach, um auf das letzte Element zu reagieren:

List<int> myList = new List<int>() {1, 2, 3, 4, 5};
Console.WriteLine("foreach version");
{
    foreach (var current in myList)
    {
        Console.WriteLine(current);
    }
}
Console.WriteLine("equivalent that reacts to last element");
{
    var enumerator = myList.GetEnumerator();
    if (enumerator.MoveNext() == true) // Corner case: empty list.
    {
        while (true)
        {
            int current = enumerator.Current;

            // Handle current element here.
            Console.WriteLine(current);

            bool ifLastElement = (enumerator.MoveNext() == false);
            if (ifLastElement)
            {
                // Cleanup after last element
                Console.WriteLine("[last element]");
                break;
            }
        }
    }
    enumerator.Dispose();
}
Contango
quelle
1

Speichern Sie einfach den vorherigen Wert und arbeiten Sie damit in der Schleife. Am Ende ist der 'vorherige' Wert das letzte Element, sodass Sie anders damit umgehen können. Keine Zählung oder spezielle Bibliotheken erforderlich.

bool empty = true;
Item previousItem;

foreach (Item result in Model.Results)
{
    if (!empty)
    {
        // We know this isn't the last item because it came from the previous iteration
        handleRegularItem(previousItem);
    }

    previousItem = result;
    empty = false;
}

if (!empty)
{
    // We know this is the last item because the loop is finished
    handleLastItem(previousItem);
}
Voltrevo
quelle
1

Sie können einfach eine for-Schleife verwenden und es ist nicht erforderlich, eine zusätzliche ifim forKörper hinzuzufügen :

for (int i = 0; i < Model.Results.Count - 1; i++) {
    var item = Model.Results[i];
}

Der -1im forZustand befindliche sorgt dafür, dass der letzte Gegenstand übersprungen wird.

Alisson
quelle
Das -1 in der for-Schleife sorgt nicht dafür, dass das letzte Element übersprungen wird. Sie würden eine IndexOutOfRangeException erhalten, wenn Sie die -1 nicht einschließen würden.
Jaa H
0

Um für jedes Element außer dem letzten etwas zusätzliches zu tun, kann ein funktionsbasierter Ansatz verwendet werden.

delegate void DInner ();

....
    Dinner inner=delegate 
    { 
        inner=delegate 
        { 
            // do something additional
        } 
    }
    foreach (DataGridViewRow dgr in product_list.Rows)
    {
        inner()
        //do something
    }
}

Dieser Ansatz hat offensichtliche Nachteile: weniger Codeklarheit für komplexere Fälle. Das Anrufen von Delegierten ist möglicherweise nicht sehr effektiv. Die Fehlerbehebung ist möglicherweise nicht ganz einfach. Die gute Seite - Codierung macht Spaß!

Trotzdem würde ich vorschlagen, in trivialen Fällen Plain for Loops zu verwenden, wenn Sie wissen, dass die Anzahl Ihrer Sammlungen nicht besonders langsam ist.

dmitry
quelle
0

Eine andere Möglichkeit, die ich nicht gesehen habe, ist die Verwendung einer Warteschlange. Es ist analog zu einer Möglichkeit, eine SkipLast () -Methode zu implementieren, ohne mehr als nötig zu iterieren. Auf diese Weise können Sie dies auch für eine beliebige Anzahl von letzten Elementen tun.

public static void ForEachAndKnowIfLast<T>(
    this IEnumerable<T> source,
    Action<T, bool> a,
    int numLastItems = 1)
{
    int bufferMax = numLastItems + 1;
    var buffer = new Queue<T>(bufferMax);
    foreach (T x in source)
    {
        buffer.Enqueue(x);
        if (buffer.Count < bufferMax)
            continue; //Until the buffer is full, just add to it.
        a(buffer.Dequeue(), false);
    }
    foreach (T item in buffer)
        a(item, true);
}

Um dies zu nennen, gehen Sie wie folgt vor:

Model.Results.ForEachAndKnowIfLast(
    (result, isLast) =>
    {
        //your logic goes here, using isLast to do things differently for last item(s).
    });
rrreee
quelle
0
     List<int> ListInt = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };


                int count = ListInt.Count;
                int index = 1;
                foreach (var item in ListInt)
                {
                    if (index != count)
                    {
                        Console.WriteLine("do something at index number  " + index);
                    }
                    else
                    {
                        Console.WriteLine("Foreach loop, this is the last iteration of the loop " + index);
                    }
                    index++;

                }
 //OR
                int count = ListInt.Count;
                int index = 1;
                foreach (var item in ListInt)
                {
                    if (index < count)
                    {
                        Console.WriteLine("do something at index number  " + index);
                    }
                    else
                    {
                        Console.WriteLine("Foreach loop, this is the last iteration of the loop " + index);
                    }
                    index++;

                }
Zoyeb Shaikh
quelle
0

Sie können eine speziell dafür vorgesehene Erweiterungsmethode erstellen:

public static class EnumerableExtensions {
    public static bool IsLast<T>(this List<T> items, T item)
        {
            if (items.Count == 0)
                return false;
            T last = items[items.Count - 1];
            return item.Equals(last);
        }
    }

und Sie können es so verwenden:

foreach (Item result in Model.Results)
{
    if(Model.Results.IsLast(result))
    {
        //do something in the code
    }
}
A. Morel
quelle
0

Basierend auf der Antwort von @ Shimmy habe ich eine Erweiterungsmethode erstellt, die die Lösung ist, die jeder möchte. Es ist einfach, benutzerfreundlich und durchläuft die Sammlung nur einmal.

internal static class EnumerableExtensions
{
    public static void ForEachLast<T>(this IEnumerable<T> collection, Action<T>? actionExceptLast = null, Action<T>? actionOnLast = null)
    {
        using var enumerator = collection.GetEnumerator();
        var isNotLast = enumerator.MoveNext();
        while (isNotLast)
        {
            var current = enumerator.Current;
            isNotLast = enumerator.MoveNext();
            var action = isNotLast ? actionExceptLast : actionOnLast;
            action?.Invoke(current);
        }
    }
}

Dies funktioniert auf jedem IEnumerable<T>. Die Verwendung sieht folgendermaßen aus:

var items = new[] {1, 2, 3, 4, 5};
items.ForEachLast(i => Console.WriteLine($"{i},"), i => Console.WriteLine(i));

Die Ausgabe sieht aus wie:

1,
2,
3,
4,
5

Darüber hinaus können Sie dies zu einer SelectStilmethode machen. Verwenden Sie diese Erweiterung dann in der ForEach. Dieser Code sieht folgendermaßen aus:

internal static class EnumerableExtensions
{
    public static void ForEachLast<T>(this IEnumerable<T> collection, Action<T>? actionExceptLast = null, Action<T>? actionOnLast = null) =>
        // ReSharper disable once IteratorMethodResultIsIgnored
        collection.SelectLast(i => { actionExceptLast?.Invoke(i); return true; }, i => { actionOnLast?.Invoke(i); return true; }).ToArray();

    public static IEnumerable<TResult> SelectLast<T, TResult>(this IEnumerable<T> collection, Func<T, TResult>? selectorExceptLast = null, Func<T, TResult>? selectorOnLast = null)
    {
        using var enumerator = collection.GetEnumerator();
        var isNotLast = enumerator.MoveNext();
        while (isNotLast)
        {
            var current = enumerator.Current;
            isNotLast = enumerator.MoveNext();
            var selector = isNotLast ? selectorExceptLast : selectorOnLast;
            //https://stackoverflow.com/a/32580613/294804
            if (selector != null)
            {
                yield return selector.Invoke(current);
            }
        }
    }
}
Michael Yanni
quelle
-1

Wir können den letzten Artikel in der Schleife überprüfen.

foreach (Item result in Model.Results)
{
    if (result==Model.Results.Last())
    {
        // do something different with the last item
    }
}
Bashir Momen
quelle
-2
foreach (DataRow drow in ds.Tables[0].Rows)
            {
                cnt_sl1 = "<div class='col-md-6'><div class='Slider-img'>" +
                          "<div class='row'><img src='" + drow["images_path"].ToString() + "' alt='' />" +
                          "</div></div></div>";
                cnt_sl2 = "<div class='col-md-6'><div class='Slider-details'>" +
                          "<p>" + drow["situation_details"].ToString() + "</p>" +
                          "</div></div>";
                if (i == 0)
                {
                    lblSituationName.Text = drow["situation"].ToString();
                }
                if (drow["images_position"].ToString() == "0")
                {
                    content += "<div class='item'>" + cnt_sl1 + cnt_sl2 + "</div>";
                    cnt_sl1 = "";
                    cnt_sl2 = "";
                }
                else if (drow["images_position"].ToString() == "1")
                {
                    content += "<div class='item'>" + cnt_sl2 + cnt_sl1 + "</div>";
                    cnt_sl1 = "";
                    cnt_sl2 = "";
                }
                i++;
            }

quelle
(!) Egal wie gut oder wie schlecht Ihr Code ist. Ohne Erklärung hat es normalerweise keinen Wert.
AlexMelw
Auch scheint es viel überentwickelt.
Mecograph
-3

Sie können dies tun:

foreach (DataGridViewRow dgr in product_list.Rows)
{
    if (dgr.Index == dgr.DataGridView.RowCount - 1)
    {
        //do something
    }
}
Sheharyar
quelle