[C#] Foreach 루프에서 루프의 마지막 반복을 결정하십시오.

나는이 foreach루프를 마지막 항목이에서 선택할 때 몇 가지 논리를 실행해야합니다 List예 :

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

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



답변

마지막 요소로 무언가를 해야하는 경우 (마지막 요소 와 다른 것이 아니라 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 루프는 어떻습니까?

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

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

또는 Linq와 foreach를 사용하십시오.

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


답변

Last()특정 유형을 사용 하면 전체 컬렉션을 통해 루프됩니다!
의미 foreach하고 호출 Last()하면 두 번 반복 됩니다! 큰 컬렉션에서는 피하고 싶습니다.

그런 다음 해결책은 do 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가 보여 주듯이 Linq는 일할 것입니다. Last ()를 사용하여 열거 가능한 마지막 항목에 대한 참조를 얻으십시오. 해당 참조로 작업하지 않는 한 일반 코드를 수행하지만 해당 참조로 작업하는 경우 추가 작업을 수행하십시오. 단점은 항상 O (N) 복잡성이라는 것입니다.

대신 Count () (IEnumerable도 ICollection 인 경우 O (1)이며 대부분의 내장 IEnumerable에 해당되는 경우)를 사용하고 foreach를 카운터와 하이브리드로 사용할 수 있습니다.

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


답변

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

  }
}


답변

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