2009-02-13 25 views
5

Ho due o più matrici: una con ID, uno o più con valori stringa. Voglio unirli in una tabella hash così posso cercare i valori per ID.Unione/unione di matrici in C#

La seguente funzione fa il lavoro, ma una versione più breve e più dolce (LINQ?) Sarebbe bello:

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

Tutte le idee?

risposta

3

Questo è molto inefficace al momento: tutte quelle chiamate a ElementAt potrebbero passare attraverso l'intera sequenza (per quanto è necessario) ogni volta. (Dipende dall'implementazione della sequenza.)

Tuttavia, non sono affatto sicuro di aver nemmeno capito cosa stia facendo questo codice (l'uso di cicli foreach farebbe sicuramente più chiarezza, come farebbe iterando avanti invece che indietro . Potrebbe dare qualche input campione e risultati attesi

EDIT:?. va bene, mi sembra di vedere che cosa sta succedendo qui, si sta effettivamente facendo perno valueCollections ho il sospetto che si vorrà qualcosa di simile:

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

E 'piuttosto brutto, se si può fare di idCollection una matrice per iniziare, sarebbe francamente più facile

EDIT: Okay, assumendo possiamo usare array invece:

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

ho corretto (si spera) un bug nella prima versione - mi è stato sempre confuso tra i quali bit dei valori è un array e che non era. La seconda versione non è dichiarativa, ma penso che sia più chiara, personalmente.

+0

> tutte le chiamate al ElementAt andranno attraverso l'intera sequenza Questo dipende da ciò che sta attuando l'IEnumerable. Se è effettivamente un array, sarà molto veloce. Se si utilizza qualcosa come una lista collegata è inefficiente. –

+0

@Dan: Non ero a conoscenza del fatto che Enumerable.ElementAt abbia verificato un'implementazione di IList . Questo fa una grande differenza. Modificherà. –

+0

Nel mio caso, sia idCollection che valueCollections sono array. Ho solo cercato di renderlo più generico - un classico esempio di inutile tentativo di anticipare le esigenze future. Questi sono matrici di dati di moduli che ho bisogno di unire e non saranno mai altro che matrici. – Bergius

1

A meno che non manchi qualcosa, non è necessario il codice per duplicare gli array: sono oggetti a se stanti e non scompariranno inaspettatamente.

Utilizzare Select() per combinare insieme i valori utilizzando una classe anonima, quindi ToDictionary() per raggrupparli.

Prova questo:

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

Annotare il tipo di ritorno utilizza invece di IEnumerable string [] - IMHO troverete questo più flessibile rispetto all'utilizzo di matrici.

Inoltre, il tipo di ritorno utilizza un'interfaccia.

Aggiornamento: John Skeet ha fatto un buon punto (vedi commento sotto) che gli array sono mutabili e questo potrebbe essere un problema. Vedi sotto per un cambiamento facile per creare array fresche:

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

Ci sono due motivi per "ricreare" gli array: il primo è che in realtà non sta prendendo gli array esistenti, sta creando un array prendendo un elemento da * ogni * array. Tuttavia, anche se non lo fosse, potrebbero essere * mutati * più tardi. Può o non può essere un problema. –

2

Come su:

 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 leggermente modificare la risposta al piattello (non ha funzionato per me):

 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

Ecco un po 'di eleganza. È più lungo di quanto mi piace, ma è molto esplorabile.

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

Ed ecco i metodi di supporto che ho utilizzato.

Disclaimer: se chiami Pivot con una struttura non rettangolare, non so/mi interessa cosa accadrà.

0

Questo non è così generale come richiesto ma unisce due enumerabili in una enumerabile di coppie. È piuttosto banale trasformare quel risultato enumerabile in un dizionario.

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