programing

각 루프에 대해 루프의 마지막 반복을 결정합니다.

testmans 2023. 5. 25. 21:38
반응형

각 루프에 대해 루프의 마지막 반복을 결정합니다.

나는 있습니다foreach루프 및 마지막 항목이 선택될 때 몇 가지 논리를 실행해야 합니다.List예:

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

루프와 카운터에 사용하지 않고 어떤 루프가 마지막인지 알 수 있습니까?

마지막 요소와 다른 요소를 사용하는 것이 아니라 마지막 요소를 사용하는 것이 필요한 경우 LINQ를 사용하면 다음과 같은 이점을 얻을 수 있습니다.

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

마지막 요소로 다른 작업을 수행해야 하는 경우 다음과 같은 작업이 필요합니다.

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

제품이 반품된 제품과 동일한지 확인하려면 사용자 지정 비교기를 작성해야 할 수도 있습니다.Last().

이 접근 방식은 다음과 같이 주의하여 사용해야 합니다.Last컬렉션을 통해 반복해야 할 수도 있습니다.이는 소규모 수집에서는 문제가 되지 않을 수 있지만 크기가 커지면 성능에 영향을 미칠 수 있습니다.목록에 중복 항목이 포함된 경우에도 실패합니다.이 경우에는 다음과 같은 방법이 더 적합할 수 있습니다.

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

루프용으로 만들어진 좋은 구식 제품은 어떻습니까?

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

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

또는 각각의 Linq 및 for를 사용합니다.

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

사용.Last()특정 유형에서 전체 컬렉션을 순환합니다!
그 말은 만약 당신이 만든다면.foreach와 콜Last()당신은 두 루프했습니다!당신이 많은 수집품에서 피하고 싶어할 것이라고 확신합니다.

그렇다면 해결책은 다음과 같습니다.while루프:

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

수집 유형이 유형이 아닌 경우IList<T>Last()함수는 모든 수집 요소를 통해 반복됩니다.

시험

컬렉션에서 랜덤 액세스를 제공하는 경우(예: 구현)IList<T>), 다음과 같이 항목을 확인할 수도 있습니다.

if(collection is IList<T> list)
  return collection[^1]; //replace with collection.Count -1 in pre-C#8 apps

Chris가 보여주듯이, Linkq는 작동할 것입니다. Last()를 사용하여 열거형의 마지막 참조를 가져오고, 해당 참조로 작업하지 않는 한 일반 코드를 수행하지만, 해당 참조로 작업하는 경우 추가 작업을 수행합니다.단점은 항상 O(N)-복잡하다는 것입니다.

대신 Count()(IE 숫자가 I 모음이기도 한 경우 O(1)이며, 이는 대부분의 일반적인 내장 IE 숫자에 해당합니다.)를 사용하여 각 항목을 카운터와 하이브리드할 수 있습니다.

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

Shimmy가 지적했듯이 Last()를 사용하면 성능 문제가 발생할 수 있습니다. 예를 들어, LINQ 식의 실시간 결과가 수집된 경우입니다.여러 번 반복되는 것을 방지하기 위해 다음과 같은 "ForEach" 확장 방법을 사용할 수 있습니다.

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

확장 방법은 다음과 같습니다(추가 보너스로 인덱스와 첫 번째 요소를 확인하는 경우에도 알려줍니다).

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 답변을 더욱 개선하여 다른 답변을 쌓을 수 있습니다.IEnumerable다음과 같은 여러 번의 반복 및 람다를 방지합니다.

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

확장 방법 구현:

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

반복기 구현에서는 이를 제공하지 않습니다.은 컬션이같수있다습니일 수.IListO(1)의 인덱스를 통해 액세스할 수 있습니다.그런 경우에는 일반적인 방법을 사용할 수 있습니다.forloop:

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

카운트를 알고 있지만 인덱스를 통해 액세스할 수 없는 경우(따라서 결과는ICollection), , , 를을 셀 수 i에 시대에foreach몸의 길이와 비교하는 것.

이 모든 것이 완벽하게 우아하지는 않습니다.크리스의 해결책은 제가 지금까지 본 것 중에 가장 좋을 것입니다.

가장 좋은 방법은 루프 후에 해당 단계를 실행하는 것입니다. 예를 들어,

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

//Post execution logic

아니면 마지막 결과에 대해 뭔가 조치를 취해야 할 경우

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

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

//Execute logic on lastItem here

조금 더 간단한 접근법은 어떨까요?

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

컬렉션의 중복 항목에 대해 승인된 답변이 작동하지 않습니다.설정된 경우foreach고유한 인덱싱 변수를 추가하면 됩니다.

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

각각의 Linq 및 for를 사용합니다.

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/en/q/7213ce

@Shimmy의 답변을 바탕으로 모두가 원하는 솔루션인 확장 방법을 만들었습니다.이것은 간단하고 사용하기 쉬우며 컬렉션을 한 번만 순환합니다.

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

이 작업은 모든 작업에 적용됩니다.IEnumerable<T>사용량은 다음과 같습니다.

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

출력은 다음과 같습니다.

1,
2,
3,
4,
5

을 추로가, 당은이만수있들다습니것을신▁a▁this로 만들 수 있습니다.Select문체법그런 다음 해당 확장을 에서 다시 사용합니다.ForEach다음과 .

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

이전 값을 저장하고 루프 내에서 작업하기만 하면 됩니다.그러면 마지막에 '이전' 값이 마지막 항목이 되어 다르게 처리할 수 있습니다.계수나 특수 라이브러리가 필요하지 않습니다.

bool empty = true;
Item previousItem;

foreach (Item result in Model.Results)
{
    // Alternatively, check if previousItem == null
    // if your Enumerable can't contain nulls
    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);
}

Jon Skeet의 우수한 코드를 약간 조정하면 이전 항목과 다음 항목에 대한 액세스를 허용하여 더 스마트하게 만들 수 있습니다.물론 이것은 구현에서 한 항목을 미리 읽어야 한다는 것을 의미합니다.성능상의 이유로 이전 및 다음 항목은 현재 반복 항목에 대해서만 유지됩니다.그건 이런 식이다:

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

        }
    }
}

제가 게시된 것을 보지 못한 또 다른 방법은 큐를 사용하는 것입니다.이는 SkipLast() 메서드를 필요 이상 반복하지 않고 구현하는 방법과 비슷합니다.이 방법을 사용하면 마지막 항목의 수에 관계없이 이 작업을 수행할 수 있습니다.

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

이를 위해 다음을 수행합니다.

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

변환 방법foreach마지막 요소에 반응합니다.

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

당신은 그냥 for 루프를 사용할 수 있으며 추가할 필요가 없습니다.if에의 에.for표시된 내용:

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

-1에 시대에for조건은 마지막 항목을 건너뛰는 것을 처리합니다.

".last()"는 저를 위해 작동하지 않았기 때문에, 저는 다음과 같은 일을 해야 했습니다.

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

이를 위해 특별히 전용 확장 방법을 만들 수 있습니다.

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

다음과 같이 사용할 수 있습니다.

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

마지막 요소를 제외한 각 요소에 추가적인 작업을 수행하려면 함수 기반 접근 방식을 사용할 수 있습니다.

delegate void DInner ();

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

이 접근 방식에는 명백한 단점이 있습니다. 즉, 복잡한 경우에는 코드 명확성이 떨어집니다.딜러에게 전화를 거는 것은 그다지 효과적이지 않을 수 있습니다.문제 해결이 쉽지 않을 수 있습니다.긍정적인 면 - 코딩은 재미있습니다!

그렇기는 하지만, 수집의 수가 심각하게 느리지 않다는 것을 안다면 사소한 경우에 루프에 플레인을 사용하는 것을 제안합니다.

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

                }

다음과 같이 할 수 있습니다.

foreach (DataGridViewRow dgr in product_list.Rows)
{
    if (dgr.Index == dgr.DataGridView.RowCount - 1)
    {
        //do something
    }
}
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++;
            }

언급URL : https://stackoverflow.com/questions/7476174/foreach-loop-determine-which-is-the-last-iteration-of-the-loop

반응형