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ループとカウンターを使用せずに、どのループが最後かを知ることはできますか?
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
}
}
var last = Model.Result[Model.Result.Count - 1];
速く使用するよりもために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
}
}
Model.Results
がである場合、この巧妙なソリューションは使用できませんIEnumerable
。Count()
ループの前に呼び出すこともできますが、シーケンスの完全な反復が発生する可能性があります。
Last()
特定のタイプで使用すると、コレクション全体がループします。
つまり、foreach
を呼び出してを呼び出すとLast()
、2回ループすることになります。大きなコレクションでは避けたいと思います。
次に、解決策は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()
コレクションがランダムアクセスを提供する場合(たとえば、implements IList<T>
)、次のようにアイテムをチェックすることもできます。
if(collection is IList<T> list)
return collection[^1]; //replace with collection.Count -1 in pre-C#8 apps
using
ステートメントが必要ですか?これは、オブジェクトがオペレーティングシステムのリソースを処理する場合にのみ必要であり、マネージデータ構造では必要ないと考えました。
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))
{
}
}
foreach
ます。このように:var lastItem = objList.LastOrDeafault();
。次に、foreach
ループの内側から次のように確認できますf (item.Equals(lastItem)) { ... }
。元の答えでは、objList.LastOrDefault()
「foreach」の反復ごとにコレクションが反復されます(ポリノミアルの複雑さが関係しています)。
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; }
}
}
ダニエルウルフの答えをさらに向上させる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;
}
}
}
foreach
、これは改善です。
イテレータ実装はそれを提供しません。あなたのコレクションはIList
、O(1)のインデックスを介してアクセスできるものである可能性があります。その場合は、通常のfor
ループを使用できます。
for(int i = 0; i < Model.Results.Count; i++)
{
if(i == Model.Results.Count - 1) doMagic();
}
あなたは、カウントを知っているが、指標(したがって、結果は経由でのアクセスができない場合ICollection
)、あなたはインクリメントすることにより、自分自身をカウントすることができますi
でforeach
のボディと長さとの比較します。
これらすべては完全にエレガントではありません。クリスの解決策は、私がこれまで見てきた中で最も優れているかもしれません。
少し単純なアプローチについてはどうでしょう。
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(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
Linqとforeachを使用:
foreach (Item result in Model.Results)
{
if (Model.Results.IndexOf(result) == Model.Results.Count - 1) {
// this is the last item
}
}
Jon Skeetの優れたコードに少し調整を加えると、前のアイテムと次のアイテムへのアクセスを許可することで、さらにスマートにすることができます。もちろん、これは実装の1つの項目を先読みする必要があることを意味します。パフォーマンス上の理由から、前のアイテムと次のアイテムは現在の反復アイテムでのみ保持されます。こんなふうになります:
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<T> 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
}
}
}
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();
}
以前の値を保存して、ループ内で処理するだけです。次に、最後に「前の」値が最後のアイテムになり、別の方法で処理できます。カウントや特別なライブラリは必要ありません。
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);
}
Jon SkeetはSmartEnumerable<T>
この問題を解決するためにしばらく前にタイプを作成しました。あなたはそれがここに実装されているのを見ることができます:
http://codeblog.jonskeet.uk/2007/07/27/smart-enumerations/
ダウンロードするには:http : //www.yoda.arachsys.com/csharp/miscutil/
最後の要素以外の各要素に何かを行うには、関数ベースのアプローチを使用できます。
delegate void DInner ();
....
Dinner inner=delegate
{
inner=delegate
{
// do something additional
}
}
foreach (DataGridViewRow dgr in product_list.Rows)
{
inner()
//do something
}
}
このアプローチには明らかな欠点があります。より複雑なケースではコードがわかりにくくなります。デリゲートを呼び出すことはあまり効果的ではないかもしれません。トラブルシューティングは簡単ではないかもしれません。明るい面-コーディングは楽しいです!
そうは言っても、コレクションのカウントがそれほど遅くないことがわかっている場合は、些細なケースでは単純なforループを使用することをお勧めします。
投稿が表示されなかった別の方法は、キューを使用することです。これは、必要以上に繰り返すことなく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).
});
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++;
}
これに特化した拡張メソッドを作成できます。
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
}
}
@Shimmyの応答に基づいて、私は誰もが望むソリューションである拡張メソッドを作成しました。シンプルで使いやすく、コレクションをループするのは1回だけです。
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
さらに、これを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);
}
}
}
}
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++;
}