webentwicklung-frage-antwort-db.com.de

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

Ich habe eine foreach-Schleife und muss einige Logik ausführen, wenn das letzte Element aus der List ausgewählt wird, 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 zuletzt verwendet wird, ohne for-Schleife und Zähler zu verwenden? 

194
mishap

Wenn Sie nur etwas mit dem letzten Element tun müssen (im Gegensatz zu etwas anders mit dem letzten Element, dann 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, benötigen Sie Folgendes:

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

Sie müssen jedoch wahrscheinlich einen benutzerdefinierten Vergleicher schreiben, um sicherzustellen, dass das Element mit dem von Last() zurückgegebenen Element übereinstimmt.

Dieser Ansatz sollte mit Vorsicht angewendet werden, da Last möglicherweise die Sammlung durchlaufen muss. Dies ist zwar für kleine Sammlungen kein Problem, aber wenn sie groß werden, kann dies Auswirkungen auf die Leistung haben. Es schlägt auch fehl, wenn die Liste doppelte Elemente enthält. In diesen Fällen kann so etwas geeigneter sein:

int totalCount = result.Count();
for (int count = 0; count < totalCount; count++)
{
    Item result = Model.Results[count];
    count++;
    // do something with each item
    if (count == totalCount)
    {
        // do something different with the last item
    }
    else
    {
        // do something different with every item but the last
    }
}
242
ChrisF

Wie wäre es mit einer guten altmodischen for-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 dem foreach:

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

Wie Chris zeigt, wird Linq arbeiten. Verwenden Sie Last (), um einen Verweis auf den letzten Eintrag in der Aufzählung zu erhalten. Wenn Sie nicht mit diesem Verweis arbeiten, geben Sie Ihren normalen Code ein. Wenn Sie jedoch mit diesem Verweis arbeiten, müssen Sie etwas extra tun. Der Nachteil ist, dass es immer O (N) -Komplexität ist.

Sie können stattdessen count () (das ist O(1)) verwenden, wenn das IEnumerable auch eine ICollection ist (dies gilt für die meisten der üblichen integrierten IEnumerables) und das 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
 }
35
KeithS

Wenn Sie Last() für bestimmte Typen verwenden, wird die gesamte Sammlung durchlaufen.
Das heißt, wenn Sie eine foreach machen und Last() aufrufen, haben Sie zweimal geschlungen!, die Sie sicher in großen Sammlungen vermeiden möchten.

Dann ist die Lösung eine do while-Schleife:

using (var enumerator = collection.GetEnumerator())
{

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

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

    //process item

    last = !enumerator.MoveNext();        

    //process item extension according to flag; flag means item

  }
}

Prüfung

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

28
Shimmy
foreach (var item in objList)
{
  if(objList.LastOrDefault().Equals(item))
  {

  }
}
18

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 Erweiterungsmethode "ForEach" wie folgt 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 und wenn Sie das erste Element betrachten) mitgeteilt:

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; }
    }
}
10
Daniel Wolf

Die Iterator-Implementierung bietet das nicht. Ihre Sammlung kann eine IList sein, auf die über einen Index in O (1) zugegriffen werden kann. In diesem Fall können Sie eine normale for-Schleife 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 eine ICollection), können Sie sich selbst zählen, indem Sie eine i im Körper der foreach inkrementieren und mit der Länge vergleichen.

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

6
Matthias Meid

Was ist mit etwas einfacherem 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);
5
faisal

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

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

//Post execution logic

Oder wenn Sie etwas zum letzten 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
4
Dustin Hodges

Die akzeptierte Antwort funktioniert nicht für Duplikate in der Sammlung. Wenn Sie auf foreach eingestellt sind, 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++;
}
2
Ehryk

Verbesserung der Daniel Wolf-Antwort Sie könnten noch eine weitere IEnumerable stapeln, um mehrere Iterationen und Lambdas zu vermeiden.

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;
        }
    }
}
2
Fabricio Godoy

".Last ()" hat für mich nicht funktioniert, also musste ich so etwas tun:

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 */
);
1
itcropper

Durch geringfügige Anpassungen des exzellenten Codes von Jon Skeet können Sie ihn sogar intelligenter gestalten, indem Sie den Zugriff auf den vorherigen und den nächsten Eintrag ermöglichen. Dies bedeutet natürlich, dass Sie 1 Element in der Implementierung vorlesen müssen. Aus Leistungsgründen werden der vorherige und der nächste Artikel nur für den aktuellen Iterationsartikel 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

        }
    }
}
1
Edwin

Sie können eine speziell dafür entwickelte 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 du kannst es so verwenden:

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

Um für jedes Element außer dem letzten Element etwas 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 Code-Klarheit für komplexere Fälle. Das Anrufen von Delegierten ist möglicherweise nicht sehr effektiv. Die Fehlerbehebung ist möglicherweise nicht ganz einfach. Die helle Seite - Codieren macht Spaß!

Wenn Sie wissen, dass die Anzahl Ihrer Sammlungen nicht sehr langsam ist, würde ich vorschlagen, für Schleifen in trivialen Fällen plain zu verwenden.

0
dmitry

Jon Skeet hat vor einiger Zeit einen SmartEnumerable<T>-Typ erstellt, um genau dieses Problem zu lösen. Sie können die Implementierung hier sehen:

http://codeblog.jonskeet.uk/2007/07/27/smart-enumerations/

Zum Herunterladen: http://www.yoda.arachsys.com/csharp/miscutil/

0
Spencer Ruport
     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++;

                }
0
Zoyeb Shaikh

Eine andere Möglichkeit, die ich nicht gepostet 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 der letzten Elemente 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 das anzurufen, führen Sie folgende Schritte aus:

Model.Results.ForEachAndKnowIfLast(
    (result, isLast) =>
    {
        //your logic goes here, using isLast to do things differently for last item(s).
    });
0
rrreee

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

0
HanMyintTun

Speichern Sie einfach den vorherigen Wert und arbeiten Sie damit innerhalb 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);
}
0
voltrevo

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();
}
0
Contango

Sie können einfach eine for-Schleife verwenden und es ist nicht notwendig, eine zusätzliche if im for-Body hinzuzufügen:

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

Der -1 in der for-Bedingung sorgt dafür, dass das letzte Element übersprungen wird.

0
Alisson