2009-02-13 25 views
5

Tengo dos o más matrices, una con ID, una o más con valores de cadena. Quiero fusionarlos en una tabla hash para que pueda buscar valores por ID.unir/unir matrices en C#

La siguiente función hace el trabajo, pero una versión más corta y más dulce (LINQ?) Sería bueno:

Dictionary<int, string[]> MergeArrays(IEnumerable<int> idCollection, 
             params IEnumerable<string>[] valueCollections) 
{ 
    var dict = new Dictionary<int, string[]>(); 

    var idL = idCollection.Count(); 
    while (idL-- > 0) 
    { 
     dict[idCollection.ElementAt(idL)] = new string[valueCollections.Length]; 

     var vL = valueCollections.Length; 
     while (vL-- > 0) 
      dict[idCollection.ElementAt(idL)][vL] = valueCollections[vL].ElementAt(idL); 
    } 

    return dict; 
} 

¿Alguna idea?

Respuesta

3

Eso es muy ineficiente en este momento - todas esas llamadas a ElementAt podrían estar pasando por la secuencia completa (tanto como lo necesiten) cada vez. (Depende de la implementación de la secuencia).

Sin embargo, no estoy del todo seguro de que entienda lo que este código está haciendo (usar bucles foreach casi con toda seguridad lo aclararía, al igual que avanzar hacia adelante en lugar de hacia atrás . Podría dar alguna entrada de la muestra y los resultados esperados

EDIT:??. de acuerdo, me parece ver lo que está pasando aquí, estás pivotante con eficacia valueCollections sospecho que querrá algo como:

static Dictionary<int, string[]> MergeArrays(
    IEnumerable<int> idCollection, 
    params IEnumerable<string>[] valueCollections) 
{ 
    var valueCollectionArrays = valueCollections.Select 
     (x => x.ToArray()).ToArray(); 
    var indexedIds = idCollection.Select((Id, Index) => new { Index, Id }); 

    return indexedIds.ToDictionary(x => Id, 
     x => valueCollectionArrays.Select(array => array[x.Index]).ToArray()); 
} 

Aunque es bastante feo. Si puedes hacer de idCollection una matriz para empezar, francamente sería más fácil.

EDIT: Bueno, suponiendo que podemos utilizar matrices en su lugar:

static Dictionary<int, string[]> MergeArrays(
    int[] idCollection, 
    params string[][] valueCollections) 
{ 
    var ret = new Dictionary<int, string[]>(); 
    for (int i=0; i < idCollection.Length; i++) 
    { 
     ret[idCollection[i]] = valueCollections.Select 
      (array => array[i]).ToArray(); 
    } 
    return ret; 
} 

He corregido (esperemos) un error en la primera versión - Me estaba confundido entre los cuales poco de los valores era una matriz y que no fue La segunda versión no es tan declarativa, pero creo que es más clara, personalmente.

+0

> todas esas llamadas a ElementAt van a ir a través de toda la secuencia Eso depende de lo que está llevando a cabo el IEnumerable. Si es realmente una matriz, será muy rápido. Si usa algo así como una lista vinculada, es ineficiente. –

+0

@Dan: no sabía que Enumerable.ElementAt comprobó una implementación de IList . Eso hace una gran diferencia. Editaré –

+0

En mi caso, tanto idCollection como valueCollections son matrices. Solo traté de hacerlo más genérico: un ejemplo clásico de tratar inútilmente de anticipar las necesidades futuras. Estas son matrices de datos de formulario a las que debo unirme, y nunca serán nada más que matrices. – Bergius

1

A menos que me falta algo, no necesita el código para duplicar las matrices, son objetos en sí mismos y no van a desaparecer inesperadamente.

Use Select() para combinar los valores utilizando una clase anónima, luego ToDictionary() para embotellarlos.

Prueba esto:

IDictionary<int, IEnumerable<string>> MergeArrays2(
     IEnumerable<int> idCollection, 
     params IEnumerable<string>[] valueCollections) 
    { 
     var values = valueCollections.ToList(); 
     return idCollection.Select(
      (id, index) => new { Key = id, Value = values[index] }) 
      .ToDictionary(x => x.Key, x => x.Value); 
    } 

Nota utiliza el tipo de retorno IEnumerable en lugar de cadena [] - En mi humilde opinión se encuentra esta más flexible que la utilización de matrices.

Además, el tipo de devolución utiliza una interfaz.

Actualizado: John Skeet hizo un buen punto (ver comentario a continuación) que las matrices son mutables, y esto podría ser un problema. Véase más abajo para un cambio fácil para crear matrices frescos:

IDictionary<int, IEnumerable<string>> MergeArrays2(
     IEnumerable<int> idCollection, 
     params IEnumerable<string>[] valueCollections) 
    { 
     var values = valueCollections.ToList(); 
     return idCollection.Select(
      (id, index) => new 
      { 
       Key = id, 
       Value = values[index].ToArray() // Make new array with values 
      }) 
      .ToDictionary(x => x.Key, x => x.Value); 
    } 
+0

Hay dos razones para "recrear" las matrices: la primera es que en realidad no está tomando las matrices existentes, sino que está creando una matriz tomando un elemento de * cada * matriz. Sin embargo, incluso si no fuera así, podrían * mutar * más tarde. Puede o no ser un problema. –

2

Cómo sobre: ​​

 public static Dictionary<int, string[]> MergeArrays2(IEnumerable<int> idCollection, 
     params IEnumerable<string>[] valueCollections) 
    { 
     var dict = new Dictionary<int, string[]>(); 
     var valEnums = (from v in valueCollections select v.GetEnumerator()).ToList(); 
     foreach (int id in idCollection) 
     { 
      var strings = new List<string>(); 
      foreach (var e in valEnums) 
       if (e.MoveNext()) 
        strings.Add(e.Current); 
      dict.Add(id, strings.ToArray()); 
     } 
     return dict; 
    } 

o editar un poco la respuesta al plato (que no funcionó para mí):

 static Dictionary<int, string[]> MergeArrays_Skeet(IEnumerable<int> idCollection,params IEnumerable<string>[] valueCollections) 
    { 
     var valueCollectionArrays = valueCollections.Select(x=>x.ToArray()).ToArray(); 
     var indexedIds = idCollection.Select((Id, Index) => new { Index, Id }); 
     return indexedIds.ToDictionary(x => x.Id,x => valueCollectionArrays.Select(array => array[x.Index]).ToArray()); 
    } 
1

Aquí hay algo de elegancia. Es más largo de lo que me gusta, pero es muy explorable.

public Dictionary<int, string[]> MergeArrays(
     IEnumerable<int> idCollection, 
     params IEnumerable<string>[] valueCollections 
      ) 
    { 
     Dictionary<int, int> ids = idCollection 
      .ToDictionaryByIndex(); 
     // 
     Dictionary<int, List<string>> values = 
      valueCollections.Select(x => x.ToList()) 
      .ToList() 
      .Pivot() 
      .ToDictionaryByIndex(); 
     // 
     Dictionary<int, string[]> result = 
      ids.ToDictionary(
       z => z.Value, 
       z => values[z.Key].ToArray() 
      ); 

     return result; 
    } 

Y aquí están los métodos de ayuda que he usado.

public static List<List<T>> Pivot<T> 
     (this List<List<T>> source) 
    { 
     return source 
      .SelectMany((it) => 
       it.Select((t, i) => new { t, i }) 
      ) 
      .GroupBy(z => z.i) 
      .Select(g => g.Select(z => z.t).ToList()) 
      .ToList(); 
    } 

    public static Dictionary<int, T> ToDictionaryByIndex<T> 
     (this IEnumerable<T> source) 
    { 
     return source 
      .Select((t, i) => new { t, i }) 
      .ToDictionary(z => z.i, z => z.t); 
    } 

responsabilidad: si se llama pivote con una estructura no rectangular, no sé/importa lo que sucederá.

0

Esto no es tan general como usted pidió pero une dos enumerables en un enumerable de pares. Es bastante trivial convertir ese resultado enumerable en un diccionario.

public static class ExtensionMethods 
{ 
    public static IEnumerable<Pair<TOuter, TInner>> InnerPair<TInner, TOuter>(this IEnumerable<TOuter> master, 
                     IEnumerable<TInner> minor) 
    { 
     if (master == null) 
      throw new ArgumentNullException("master"); 
     if (minor == null) 
      throw new ArgumentNullException("minor"); 
     return InnerPairIterator(master, minor); 
    } 

    public static IEnumerable<Pair<TOuter, TInner>> InnerPairIterator<TOuter, TInner>(IEnumerable<TOuter> master, 
                       IEnumerable<TInner> minor) 
    { 
     IEnumerator<TOuter> imaster = master.GetEnumerator(); 
     IEnumerator<TInner> iminor = minor.GetEnumerator(); 
     while (imaster.MoveNext() && iminor.MoveNext()) 
     { 
      yield return 
       new Pair<TOuter, TInner> { First = imaster.Current, Second = iminor.Current }; 
     } 
    } 
} 


public class Pair<TFirst, TSecond> 
{ 
    public TFirst First { get; set; } 
    public TSecond Second { get; set; } 
}