recorrer - matriz 5x5 c#
Iterar dos listas o matrices con una instrucción ForEach en C# (10)
Esto solo para conocimiento general:
Si tengo dos, digamos, List , y quiero iterar ambos con el mismo bucle foreach, ¿podemos hacer eso?
Editar
Solo para aclarar, quería hacer esto:
List<String> listA = new List<string> { "string", "string" };
List<String> listB = new List<string> { "string", "string" };
for(int i = 0; i < listA.Count; i++)
listB[i] = listA[i];
Pero con un foreach =)
Aquí hay un método de extensión IEnumerable <> personalizado que se puede utilizar para recorrer dos listas al mismo tiempo.
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
public static class LinqCombinedSort
{
public static void Test()
{
var a = new[] {''a'', ''b'', ''c'', ''d'', ''e'', ''f''};
var b = new[] {3, 2, 1, 6, 5, 4};
var sorted = from ab in a.Combine(b)
orderby ab.Second
select ab.First;
foreach(char c in sorted)
{
Console.WriteLine(c);
}
}
public static IEnumerable<Pair<TFirst, TSecond>> Combine<TFirst, TSecond>(this IEnumerable<TFirst> s1, IEnumerable<TSecond> s2)
{
using (var e1 = s1.GetEnumerator())
using (var e2 = s2.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext())
{
yield return new Pair<TFirst, TSecond>(e1.Current, e2.Current);
}
}
}
}
public class Pair<TFirst, TSecond>
{
private readonly TFirst _first;
private readonly TSecond _second;
private int _hashCode;
public Pair(TFirst first, TSecond second)
{
_first = first;
_second = second;
}
public TFirst First
{
get
{
return _first;
}
}
public TSecond Second
{
get
{
return _second;
}
}
public override int GetHashCode()
{
if (_hashCode == 0)
{
_hashCode = (ReferenceEquals(_first, null) ? 213 : _first.GetHashCode())*37 +
(ReferenceEquals(_second, null) ? 213 : _second.GetHashCode());
}
return _hashCode;
}
public override bool Equals(object obj)
{
var other = obj as Pair<TFirst, TSecond>;
if (other == null)
{
return false;
}
return Equals(_first, other._first) && Equals(_second, other._second);
}
}
}
Desde C # 7, puedes usar Tuples ...
int[] nums = { 1, 2, 3, 4 };
string[] words = { "one", "two", "three", "four" };
foreach (var tuple in nums.Zip(words, (x, y) => (x, y)))
{
Console.WriteLine($"{tuple.Item1}: {tuple.Item2}");
}
// or...
foreach (var tuple in nums.Zip(words, (x, y) => (Num: x, Word: y)))
{
Console.WriteLine($"{tuple.Num}: {tuple.Word}");
}
Entiendo / espero que las listas tengan la misma duración: No, su única apuesta es ir con un viejo estándar simple para bucle.
Este método funcionaría para una implementación de lista y podría implementarse como un método de extensión.
public void TestMethod()
{
var first = new List<int> {1, 2, 3, 4, 5};
var second = new List<string> {"One", "Two", "Three", "Four", "Five"};
foreach(var value in this.Zip(first, second, (x, y) => new {Number = x, Text = y}))
{
Console.WriteLine("{0} - {1}",value.Number, value.Text);
}
}
public IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(List<TFirst> first, List<TSecond> second, Func<TFirst, TSecond, TResult> selector)
{
if (first.Count != second.Count)
throw new Exception();
for(var i = 0; i < first.Count; i++)
{
yield return selector.Invoke(first[i], second[i]);
}
}
Esto se conoce como operación Zip y será compatible con .NET 4.
Con eso, podrías escribir algo como:
var numbers = new [] { 1, 2, 3, 4 };
var words = new [] { "one", "two", "three", "four" };
var numbersAndWords = numbers.Zip(words, (n, w) => new { Number = n, Word = w });
foreach(var nw in numbersAndWords)
{
Console.WriteLine(nw.Number + nw.Word);
}
Como alternativa al tipo anónimo con los campos con nombre, también puede guardar llaves utilizando un Tuple y su ayuda Tuple.Create estática:
foreach (var nw in numbers.Zip(words, Tuple.Create))
{
Console.WriteLine(nw.Item1 + nw.Item2);
}
No, tendrías que usar un for-loop para eso.
for (int i = 0; i < lst1.Count; i++)
{
//lst1[i]...
//lst2[i]...
}
No puedes hacer algo como
foreach (var objCurrent1 int lst1, var objCurrent2 in lst2)
{
//...
}
Puede usar Union o Concat, el anterior elimina duplicados, el último no
foreach (var item in List1.Union(List1))
{
//TODO: Real code goes here
}
foreach (var item in List1.Concat(List1))
{
//TODO: Real code goes here
}
Si no desea esperar .NET 4.0, puede implementar su propio método Zip. Lo siguiente funciona con .NET 2.0. Puede ajustar la implementación dependiendo de cómo desee manejar el caso donde las dos enumeraciones (o listas) tienen longitudes diferentes: esta continúa hasta el final de la enumeración más larga, y devuelve los valores predeterminados para los elementos faltantes de la enumeración más corta.
static IEnumerable<KeyValuePair<T, U>> Zip<T, U>(IEnumerable<T> first, IEnumerable<U> second)
{
IEnumerator<T> firstEnumerator = first.GetEnumerator();
IEnumerator<U> secondEnumerator = second.GetEnumerator();
while (firstEnumerator.MoveNext())
{
if (secondEnumerator.MoveNext())
{
yield return new KeyValuePair<T, U>(firstEnumerator.Current, secondEnumerator.Current);
}
else
{
yield return new KeyValuePair<T, U>(firstEnumerator.Current, default(U));
}
}
while (secondEnumerator.MoveNext())
{
yield return new KeyValuePair<T, U>(default(T), secondEnumerator.Current);
}
}
static void Test()
{
IList<string> names = new string[] { "one", "two", "three" };
IList<int> ids = new int[] { 1, 2, 3, 4 };
foreach (KeyValuePair<string, int> keyValuePair in ParallelEnumerate(names, ids))
{
Console.WriteLine(keyValuePair.Key ?? "<null>" + " - " + keyValuePair.Value.ToString());
}
}
Si quieres un elemento con el correspondiente, puedes hacerlo
Enumerable.Range(0, List1.Count).All(x => List1[x] == List2[x]);
Eso volverá verdadero si cada artículo es igual al correspondiente en la segunda lista
Si eso es casi pero no lo que quieres, sería útil si elaboras más.
También podría simplemente usar una variable entera local si las listas tienen la misma longitud:
List<classA> listA = fillListA();
List<classB> listB = fillListB();
var i = 0;
foreach(var itemA in listA)
{
Console.WriteLine(itemA + listB[i++]);
}