una tamaño saber recorrer matriz matrices imprimir for como array c# iterator enumerator

tamaño - recorrer matriz c#



¿Cómo iterar sobre dos matrices a la vez? (7)

En lugar de crear dos arreglos separados, podría crear una matriz bidimensional o un diccionario (lo cual sería mejor). Pero realmente, si funciona, no trataría de cambiarlo.

Tengo dos arreglos creados al analizar un archivo de texto. El primero contiene los nombres de columna, el segundo contiene los valores de la fila actual. Necesito iterar sobre ambas listas a la vez para construir un mapa. En este momento tengo lo siguiente:

var currentValues = currentRow.Split(separatorChar); var valueEnumerator = currentValues.GetEnumerator(); foreach (String column in columnList) { valueEnumerator.MoveNext(); valueMap.Add(column, (String)valueEnumerator.Current); }

Esto funciona bien, pero no satisface por completo mi sentido de la elegancia, y se pone realmente peludo si el número de matrices es mayor que dos (como tengo que hacer ocasionalmente). ¿Alguien tiene otro, terser modismo?


En un lenguaje funcional, normalmente encontrará una función "zip" que, con suerte, formará parte de un C # 4.0. Bart de Smet proporciona una implementación divertida de zip basada en funciones LINQ existentes:

public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>( this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> func) { return first.Select((x, i) => new { X = x, I = i }) .Join(second.Select((x, i) => new { X = x, I = i }), o => o.I, i => i.I, (o, i) => func(o.X, i.X)); }

Entonces puedes hacer:

int[] s1 = new [] { 1, 2, 3 }; int[] s2 = new[] { 4, 5, 6 }; var result = s1.Zip(s2, (i1, i2) => new {Value1 = i1, Value2 = i2});


Puede crear un enumerador de plantillas a la Pairenumerable . Personalmente, creo que es exagerado.


Si realmente está utilizando matrices, probablemente la mejor forma sea simplemente usar el ciclo convencional con índices. No es tan agradable, concedido, pero hasta donde yo sé, .NET no ofrece una mejor manera de hacerlo.

También podría encapsular su código en un método llamado zip ; esta es una función común de lista de orden superior. Sin embargo, C # que carece de un tipo de Tuple adecuado, esto es bastante complicado. Usted terminaría devolviendo un IEnumerable<KeyValuePair<T1, T2>> que no es muy agradable.

Por cierto, ¿realmente estás usando IEnumerable lugar de IEnumerable<T> o por qué IEnumerable<T> el valor Current ?


Tienes un pseudo error no obvio en tu código inicial: IEnumerator<T> extiende IDisposable por lo que debes eliminarlo. ¡Esto puede ser muy importante con los bloques iteradores! No es un problema para las matrices, pero sería con otras IEnumerable<T> .

Lo haría así:

public static IEnumerable<TResult> PairUp<TFirst,TSecond,TResult> (this IEnumerable<TFirst> source, IEnumerable<TSecond> secondSequence, Func<TFirst,TSecond,TResult> projection) { using (IEnumerator<TSecond> secondIter = secondSequence.GetEnumerator()) { foreach (TFirst first in source) { if (!secondIter.MoveNext()) { throw new ArgumentException ("First sequence longer than second"); } yield return projection(first, secondIter.Current); } if (secondIter.MoveNext()) { throw new ArgumentException ("Second sequence longer than first"); } } }

Entonces puede reutilizar esto cuando tenga la necesidad:

foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar), (column, value) => new { column, value }) { // Do something }

De forma alternativa, podría crear un tipo de Par genérico y deshacerse del parámetro de proyección en el método PairUp.

EDITAR:

Con el tipo de pareja, el código de llamada se vería así:

foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar)) { // column = pair.First, value = pair.Second }

Eso se ve tan simple como puedes obtener. Sí, necesita colocar el método de utilidad en algún lugar, como código reutilizable. A mi parecer, no es un problema. Ahora para matrices múltiples ...

Si las matrices son de tipos diferentes, tenemos un problema. No se puede expresar un número arbitrario de parámetros de tipo en una declaración genérica de método / tipo: se pueden escribir versiones de PairUp para tantos parámetros de tipo como se desee, al igual que hay delegados Action y Func para hasta 4 parámetros de delegado, pero no puedes hacerlo arbitrario.

Sin embargo, si todos los valores serán del mismo tipo, y si está contento con las matrices, es fácil. (Los modelos que no son matrices también están bien, pero no se puede hacer la verificación de longitud con anticipación). Podrías hacer esto:

public static IEnumerable<T[]> Zip<T>(params T[][] sources) { // (Insert error checking code here for null or empty sources parameter) int length = sources[0].Length; if (!sources.All(array => array.Length == length)) { throw new ArgumentException("Arrays must all be of the same length"); } for (int i=0; i < length; i++) { // Could do this bit with LINQ if you wanted T[] result = new T[sources.Length]; for (int j=0; j < result.Length; j++) { result[j] = sources[j][i]; } yield return result; } }

Entonces el código de llamada sería:

foreach (var array in Zip(columns, row, whatevers)) { // column = array[0] // value = array[1] // whatever = array[2] }

Esto implica una cierta cantidad de copia, por supuesto, estás creando una matriz cada vez. Puede cambiar eso introduciendo otro tipo como este:

public struct Snapshot<T> { readonly T[][] sources; readonly int index; public Snapshot(T[][] sources, int index) { this.sources = sources; this.index = index; } public T this[int element] { return sources[element][index]; } }

Esto probablemente sería considerado como excesivo por la mayoría sin embargo;)

Podría seguir presentando todo tipo de ideas, para ser sincero ... pero los conceptos básicos son:

  • Con un poco de trabajo reutilizable, puede hacer que el código de llamada sea más agradable
  • Para combinaciones arbitrarias de tipos, tendrá que hacer cada número de parámetros (2, 3, 4 ...) por separado debido a la forma en que funcionan los genéricos.
  • Si está contento de usar el mismo tipo para cada parte, puede hacerlo mejor

Usar IEnumerator para ambos sería bueno

var currentValues = currentRow.Split(separatorChar); using (IEnumerator<string> valueEnum = currentValues.GetEnumerator(), columnEnum = columnList.GetEnumerator()) { while (valueEnum.MoveNext() && columnEnum.MoveNext()) valueMap.Add(columnEnum.Current, valueEnum.Current); }

O crea un método de extensión

public static IEnumerable<TResult> Zip<T1, T2, TResult>(this IEnumerable<T1> source, IEnumerable<T2> other, Func<T1, T2, TResult> selector) { using (IEnumerator<T1> sourceEnum = source.GetEnumerator()) { using (IEnumerator<T2> otherEnum = other.GetEnumerator()) { while (sourceEnum.MoveNext() && columnEnum.MoveNext()) yield return selector(sourceEnum.Current, otherEnum.Current); } } }

Uso

var currentValues = currentRow.Split(separatorChar); foreach (var valueColumnPair in currentValues.Zip(columnList, (a, b) => new { Value = a, Column = b }) { valueMap.Add(valueColumnPair.Column, valueColumnPair.Value); }


si hay la misma cantidad de nombres de columna que elementos en cada fila, ¿no podría usar un ciclo for?

var currentValues = currentRow.Split(separatorChar); for(var i=0;i<columnList.Length;i++){ // use i to index both (or all) arrays and build your map }