array - foreach list object c#
Bucle Foreach, determine cuál es la última iteración del ciclo (21)
".Last ()" no funcionó para mí, así que tuve que hacer algo como esto:
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 */
);
Tengo un bucle foreach
y necesito ejecutar algo de lógica cuando se elige el último elemento de la List
, por ejemplo:
foreach (Item result in Model.Results)
{
//if current result is the last item in Model.Results
//then do something in the code
}
¿Puedo saber qué ciclo es el último sin usar el bucle y los contadores?
¡Usando Last()
en ciertos tipos recorrerá toda la colección!
Lo que significa que si haces un foreach
y llamas a Last()
, ¡has enlazado dos veces! que estoy seguro que te gustaría evitar en grandes colecciones.
Entonces la solución es usar un ciclo do while
while:
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
}
}
A menos que el tipo de recopilación sea de tipo IList<T>
la función Last()
repetirá a través de todos los elementos de la colección.
¿Qué hay de un enfoque más simple?
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);
¿Qué tal un buen bucle anticuado?
for (int i = 0; i < Model.Results.Count; i++) {
if (i == Model.Results.Count - 1) {
// this is the last item
}
}
O usando Linq y el foreach:
foreach (Item result in Model.Results)
{
if (Model.Results.IndexOf(result) == Model.Results.Count - 1) {
// this is the last item
}
}
Cómo convertir foreach
para reaccionar al último elemento:
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();
}
Como Chris muestra, Linq funcionará; simplemente use Last () para obtener una referencia a la última en el enumerable, y mientras no esté trabajando con esa referencia, entonces haga su código normal, pero si ESTÁ trabajando con esa referencia, haga lo extra. Su inconveniente es que siempre será O (N) -complejidad.
En su lugar, puede usar Count () (que es O (1) si IEnumerable también es una colección IC, esto es cierto para la mayoría de los IEnumerables incorporados comunes) e híbrido su foreach con un contador:
var i=0;
var count = Model.Results.Count();
foreach (Item result in Model.Results)
{
if(++i==count) //this is the last item
}
Como Shimmy ha señalado, el uso de Last () puede ser un problema de rendimiento, por ejemplo, si su colección es el resultado en vivo de una expresión LINQ. Para evitar iteraciones múltiples, puede usar un método de extensión "ForEach" como este:
var elements = new[] { "A", "B", "C" };
elements.ForEach((element, info) => {
if (!info.IsLast) {
Console.WriteLine(element);
} else {
Console.WriteLine("Last one: " + element);
}
});
El método de extensión se ve así (como una ventaja adicional, también le indicará el índice y si está mirando el primer elemento):
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; }
}
}
El mejor enfoque sería simplemente ejecutar ese paso después del ciclo: por ejemplo
foreach(Item result in Model.Results)
{
//loop logic
}
//Post execution logic
O si necesita hacer algo para el último resultado
foreach(Item result in Model.Results)
{
//loop logic
}
Item lastItem = Model.Results[Model.Results.Count - 1];
//Execute logic on lastItem here
Haciendo pequeños ajustes al excelente código de Jon Skeet, incluso puedes hacerlo más inteligente al permitir el acceso al ítem anterior y siguiente. Por supuesto, esto significa que tendrá que leer 1 artículo adelante en la implementación. Por motivos de rendimiento, los elementos anterior y siguiente solo se conservan para el elemento de iteración actual. Dice así:
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
}
}
}
Jon Skeet creó un tipo SmartEnumerable<T>
un tiempo para resolver este problema exacto. Puedes ver su implementación aquí:
http://codeblog.jonskeet.uk/2007/07/27/smart-enumerations/
Para descargar: http://www.yoda.arachsys.com/csharp/miscutil/
La implementación del iterador no proporciona eso. Su colección podría ser un IList
accesible a través de un índice en O (1). En ese caso, puede usar un normal for
-loop:
for(int i = 0; i < Model.Results.Count; i++)
{
if(i == Model.Results.Count - 1) doMagic();
}
Si conoce el conteo, pero no puede acceder a través de los índices (por lo tanto, el resultado es una ICollection
), puede contar usted mismo incrementando un i
en el cuerpo del foreach
y comparándolo con la longitud.
Todo esto no es perfectamente elegante. La solución de Chris puede ser la mejor que he visto hasta ahora.
La respuesta aceptada no funcionará para los duplicados en la colección. Si está configurado en foreach
, puede agregar sus propias variables de indexación.
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++;
}
Mejorando aún más la respuesta de Daniel Wolf , podrías apilar en otro IEnumerable
para evitar múltiples iteraciones y lambdas, tales como:
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);
}
}
La implementación del método de extensión:
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;
}
}
}
Otra forma, que no vi publicado, es usar una cola. Es análogo a una forma de implementar un método SkipLast () sin iterar más de lo necesario. De esta manera también le permitirá hacer esto en cualquier cantidad de últimos elementos.
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);
}
Para llamar a esto, haría lo siguiente:
Model.Results.ForEachAndKnowIfLast(
(result, isLast) =>
{
//your logic goes here, using isLast to do things differently for last item(s).
});
Para hacer algo adicional a cada elemento, excepto el último, se puede usar el enfoque basado en función.
delegate void DInner ();
....
Dinner inner=delegate
{
inner=delegate
{
// do something additional
}
}
foreach (DataGridViewRow dgr in product_list.Rows)
{
inner()
//do something
}
}
Este enfoque tiene inconvenientes aparentes: menos claridad de código para casos más complejos. Llamar a los delegados podría no ser muy efectivo. La resolución de problemas puede no ser muy fácil. ¡El lado positivo - la codificación es divertida!
Una vez dicho esto, sugeriría que se utilicen bucles simples en casos triviales, si sabe que el recuento de su colección no es terriblemente lento.
Podemos verificar el último elemento en bucle.
foreach (Item result in Model.Results)
{
if (result==Model.Results.Last())
{
// do something different with the last item
}
}
Puedes hacer así:
foreach (DataGridViewRow dgr in product_list.Rows)
{
if (dgr.Index == dgr.DataGridView.RowCount - 1)
{
//do something
}
}
Si solo necesitas hacer algo con el último elemento (a diferencia de algo diferente con el último elemento, entonces usar LINQ te ayudará aquí:
Item last = Model.Results.Last();
// do something with last
Si necesita hacer algo diferente con el último elemento, entonces necesitaría algo como:
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
}
}
Aunque probablemente necesite escribir un comparador personalizado para asegurarse de que puede decir que el artículo era el mismo que el elemento devuelto por Last()
.
Este enfoque debe usarse con precaución ya que Last
puede tener que iterar a través de la colección. Si bien esto podría no ser un problema para colecciones pequeñas, si se vuelve grande podría tener implicaciones de rendimiento.
Simplemente almacene el valor anterior y trabaje con él dentro del bucle. Luego, al final, el valor ''anterior'' será el último elemento, lo que le permitirá manejarlo de manera diferente. Sin contar o se requieren bibliotecas especiales.
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);
}
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++;
}
foreach (var item in objList)
{
if(objList.LastOrDefault().Equals(item))
{
}
}