Pregunta

Tengo dos matrices construidas al analizar un archivo de texto.La primera contiene los nombres de columna, la segunda contiene los valores de la fila actual.Necesito recorrer en ambas listas a la vez para construir un mapa.Ahora 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 acaba de satisfacer mi sentido de la elegancia, y se pone peliagudo si el número de matrices es mayor que dos (como tengo que hacer de vez en cuando).¿Alguien tiene otro, tersa modismo?

¿Fue útil?

Solución

si hay el mismo número de nombres de columna que elementos en cada fila, ¿no podría usar un bucle 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
}

Otros consejos

Tienes un no-obvio pseudo-error en su código inicial - IEnumerator<T> se extiende IDisposable así que debe deshacerse de él.Esto puede ser muy importante con iterador bloques!No es un problema para las matrices, pero sería con otros IEnumerable<T> implementaciones.

Yo 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");
        }
    }        
}

A continuación, puede volver a utilizar este siempre tiene la necesidad de:

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

Alternativamente, usted podría crear un genérico de Par tipo, y deshacerse de la proyección de parámetros en el PairUp método.

EDITAR:

Con el Par tipo, el código de llamada tendría este aspecto:

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

Que parece tan simple como usted puede conseguir.Sí, tienes que poner el método de utilidad en algún lugar, como el código reutilizable.Apenas un problema, en mi opinión.Ahora para múltiples matrices...

Si las matrices son de diferentes tipos, tenemos un problema.No se puede expresar con un número arbitrario de parámetros de tipo en un método genérico/tipo de declaración - podría escribir versiones de PairUp por muchos parámetros de tipo como usted quería, al igual que las hay Action y Func los delegados de hasta 4 delegado de parámetros - pero usted no puede hacer es arbitraria.

Si los valores serán todos del mismo tipo, sin embargo - y si usted es feliz se adhieren a las matrices - es fácil.(No de las matrices está bien también, pero no se puede hacer la comprobación de la longitud de antemano.) Usted podría 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;
    }
}

A continuación, 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 copias, por supuesto - que estás creando una matriz cada vez.Usted podría cambiar la situación mediante la introducción de 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, aunque ;)

Podría seguir viniendo para arriba con todo tipo de ideas, para ser honesto...pero los fundamentos son:

  • Con un poco de reutilizables de trabajo, puede hacer que el código de llamada más agradable
  • Para arbitrario combinaciones de tipos que tendrás que hacer cada número de parámetros (2, 3, 4...) por separado debido a la forma en que los medicamentos genéricos funciona
  • Si usted es feliz de utilizar el mismo tipo para cada parte, que se puede hacer mejor

En un lenguaje funcional, normalmente encontrará un " zip " función que con suerte formará parte de un C # 4.0. Bart de Smet proporciona una implementación divertida de zip basada en las 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});

Si realmente está usando matrices, la mejor manera es probablemente usar el bucle for convencional con índices. No es tan agradable, por supuesto, pero que yo sepa, .NET no ofrece una mejor manera de hacerlo.

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

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

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 cree 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);
}

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top