mybatis中判断foreach循环最后一个
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mybatis中判断foreach循环最后一个相关的知识,希望对你有一定的参考价值。
参考技术A mybatis 使用 foreach 在遍历数组参数的时候,在最后一次不需要一些关键字,这个时候就需要对 foreach 的最后一次循环进行判断。使用 index 进行判断。最后一次下标为 数组长度-1
foreach 循环,判断哪个是循环的最后一次迭代
【中文标题】foreach 循环,判断哪个是循环的最后一次迭代【英文标题】:Foreach loop, determine which is the last iteration of the loop 【发布时间】:2011-11-20 12:41:35 【问题描述】:我有一个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 循环和计数器,我可以知道哪个循环是最后一个吗?
【问题讨论】:
查看我的回答 here 以获得我发布到相关问题的解决方案。 相关:How do you get the index of the current iteration of a foreach loop? 【参考方案1】:如果您只需要对最后一个元素做一些事情(而不是对最后一个元素进行 不同 的事情,那么使用 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
【讨论】:
我需要的是:当循环通过它的最后一个项目时:foreach(Model.Results 中的项目结果) if (result == Model.Results.Last()) 最后; 看来你的意思差不多。 您的代码将在整个集合中迭代两次 - 如果集合不小,那就不好了。请参阅this 答案。 如果您的收藏中有重复项,这实际上不起作用。例如,如果您正在处理一个字符串集合,并且有任何重复项,那么“与最后一项不同”的代码将针对列表中最后一项的每次出现执行。 这个答案是旧的,但对于其他查看这个答案的人,您可以获得最后一个元素并确保您不必使用以下方法遍历元素:Item last = Model.Results[Model. Results.Count - 1] 列表的 count 属性不需要循环。如果列表中有重复项,则只需在 for 循环中使用迭代器变量。常规的旧 for 循环也不错。 我建议使用var last = Model.Result[Model.Result.Count - 1];
比使用Last()
更快【参考方案2】:
最好的方法可能是在循环之后执行该步骤:例如
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
【讨论】:
【参考方案3】:正如 Chris 所示,Linq 可以工作;只需使用 Last() 来获取对可枚举中最后一个的引用,只要您不使用该引用,然后执行您的正常代码,但如果您正在使用该引用,请执行您的额外操作。它的缺点是它总是 O(N) 复杂度。
您可以改为使用 Count()(如果 IEnumerable 也是 ICollection,则为 O(1);对于大多数常见的内置 IEnumerables 来说都是如此),并将您的 foreach 与计数器混合:
var i=0;
var count = Model.Results.Count();
foreach (Item result in Model.Results)
if (++i == count) //this is the last item
【讨论】:
【参考方案4】:迭代器实现不提供。您的集合可能是一个IList
,可通过 O(1) 中的索引访问。在这种情况下,您可以使用普通的for
-loop:
for(int i = 0; i < Model.Results.Count; i++)
if(i == Model.Results.Count - 1) doMagic();
如果您知道计数,但无法通过索引访问(因此,结果是 ICollection
),您可以通过在 foreach
的正文中增加 i
并将其与长度进行比较来计算自己。
这一切都不是很优雅。 Chris 的解决方案可能是迄今为止我见过的最好的解决方案。
【讨论】:
在比较你的计数器在 foreach 想法和 Chris 的解决方案中的性能时,我想知道哪个会花费更多 - 单个 Last() 调用,或者所有添加的增量操作的总和。我怀疑它会很接近。【参考方案5】:老式的 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
【讨论】:
很多人都在考虑这样一个简单的问题,而 for 循环已经完全有能力做到这一点。 :\ Linq 解决方案是我的最爱!感谢分享 这是比接受的答案更合适的答案。 任何想对字符串(或值类型)集合使用 LINQ 解决方案的人请注意:它通常不起作用,因为如果列表中的最后一个字符串也出现,== 比较将失败在列表的前面。仅当您使用保证没有重复字符串的列表时,它才会起作用。 很遗憾,如果Model.Results
是IEnumerable
,您将无法使用这个巧妙的解决方案。您可以在循环之前调用Count()
,但这可能会导致序列的完整迭代。【参考方案6】:
在某些类型上使用 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()
函数将遍历所有集合元素。
Test
如果您的收藏提供随机访问(例如实现IList<T>
),您还可以按如下方式检查您的项目。
if(collection is IList<T> list)
return collection[^1]; //replace with collection.Count -1 in pre-C#8 apps
【讨论】:
您确定枚举器需要using
语句吗?我认为只有当对象处理操作系统资源时才需要这样做,而不是托管数据结构。
IEnumerator 没有实现 IDisposable,因此使用 with 的行会引发编译时错误! +1 for the solution ,大多数时候我们不能简单地使用 for 而不是 foreach ,因为可枚举的集合项在运行时计算或者序列不支持随机访问。
generic one 确实如此。【参考方案7】:
正如 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;
【讨论】:
【参考方案8】:更简单的方法怎么样。
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 并且正在产生 o(n) 的成本,这是完全可以接受的。 尽管答案非常适合找出最后一项,它不适合 OP 的条件 "...,确定哪个是循环的最后一次迭代”。因此,您无法确定最后一次迭代实际上是最后一次迭代,因此,您无法以不同的方式处理它,甚至无法忽略它。这就是有人对你投反对票的原因。 @arviman 你很好奇。 你说得对,我完全错过了@Andrey-WD。我想修复的解决方案是在循环之前调用一次“last”(不能在循环内执行它,因为它会是 O(N^2),然后检查引用是否匹配它。【参考方案9】:".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 */
);
【讨论】:
【参考方案10】:你可以这样做:
foreach (DataGridViewRow dgr in product_list.Rows)
if (dgr.Index == dgr.DataGridView.RowCount - 1)
//do something
【讨论】:
【参考方案11】:Jon Skeet 不久前创建了一个 SmartEnumerable<T>
类型来解决这个确切的问题。你可以在这里看到它的实现:
http://codeblog.jonskeet.uk/2007/07/27/smart-enumerations/
要下载:http://www.yoda.arachsys.com/csharp/miscutil/
【讨论】:
【参考方案12】:接受的答案不适用于集合中的重复项。如果你设置在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++;
【讨论】:
【参考方案13】:对 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
【讨论】:
【参考方案14】:要对除了最后一个元素之外的每个元素做一些额外的事情,可以使用基于函数的方法。
delegate void DInner ();
....
Dinner inner=delegate
inner=delegate
// do something additional
foreach (DataGridViewRow dgr in product_list.Rows)
inner()
//do something
这种方法有明显的缺点:对于更复杂的情况,代码清晰度较低。调用代表可能不是很有效。故障排除可能并不容易。好的一面 - 编码很有趣!
话虽如此,如果您知道您的集合的计数不是非常慢,我建议在琐碎的情况下使用普通的 for 循环。
【讨论】:
【参考方案15】:var last = objList.LastOrDefault();
foreach (var item in objList)
if (item.Equals(last))
【讨论】:
您好,这是迄今为止最好的方法!简单明了。一种程序员思考的方法,一个。我们为什么不选择并越来越多地给这个+1! 最后一项只能在foreach
块之前找到一次(促进记忆)。像这样:var lastItem = objList.LastOrDeafault();
。然后从foreach
循环的内部你可以这样检查:f (item.Equals(lastItem)) ...
。在您的原始答案中,objList.LastOrDefault()
将在每次“foreach”迭代中迭代集合(涉及多项式复杂性)。
错误答案。 n^2 复杂度而不是 n。
这是不正确的,因为提到了@ShimmyWeitzhandler 的问题,不应使用。通常期望在循环之外准备语句的所有此类值。
我已经更新了答案以避免人们陷入这个陷阱。【参考方案16】:
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++;
【讨论】:
( ! ) 不管你的代码有多好或多坏。没有解释它通常没有价值。 另外,它似乎被过度设计了。【参考方案17】:我们可以检查循环中的最后一项。
foreach (Item result in Model.Results)
if (result==Model.Results.Last())
// do something different with the last item
【讨论】:
【参考方案18】:我没有看到发布的另一种方法是使用队列。它类似于实现 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).
);
【讨论】:
【参考方案19】:如何转换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();
【讨论】:
【参考方案20】:进一步改进Daniel Wolf answer,您可以叠加另一个IEnumerable
,以避免多次迭代和lambda,例如:
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
,这是一种改进。
@Servy 我的意思是。除了原始答案的单次迭代外,我还避免使用 lambdas。【参考方案21】:
只需存储先前的值并在循环中使用它。然后最后“上一个”值将是最后一项,让您以不同的方式处理它。无需计数或特殊库。
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);
【讨论】:
【参考方案22】: 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++;
【讨论】:
【参考方案23】:您可以制作一个专门用于此的扩展方法:
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
【讨论】:
【参考方案24】:您可以只使用 for 循环,无需在 for
正文中添加额外的 if
:
for (int i = 0; i < Model.Results.Count - 1; i++)
var item = Model.Results[i];
for
条件中的-1
负责跳过最后一项。
【讨论】:
@JaaH,这不是真的,因为这是<
操作,而不是 <=
操作。【参考方案25】:
使用 Linq 和 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/en/q/7213ce
【讨论】:
这仅在列表/集合具有唯一值时才有效。【参考方案26】:根据@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
另外,你可以把它变成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://***.com/a/32580613/294804
if (selector != null)
yield return selector.Invoke(current);
【讨论】:
以上是关于mybatis中判断foreach循环最后一个的主要内容,如果未能解决你的问题,请参考以下文章