2012-03-19 20 views
11

Ho bisogno di convertire della collezione in letto [,]:Come convertire lista di array in un array multidimensionale

var ret = new List<double[]>(); 

Tutti gli array nella lista hanno la stessa lunghezza. L'approccio più semplice, ret.ToArray(), produce il doppio [] [], che non è quello che voglio. Naturalmente, posso creare manualmente un nuovo array e copiare i numeri in un ciclo, ma esiste un modo più elegante?

Modifica: la mia libreria è invocata da una lingua diversa, Mathematica, che non è stata sviluppata in .Net. Non credo che la lingua possa utilizzare array frastagliati. Devo restituire un array multidimensionale.

+1

sembra un array * frastagliato * non un array 2D: sei sicuro che sia il tuo requisito dal momento che tutti gli array hanno la stessa lunghezza? – BrokenGlass

+1

probabilmente sarebbe più elegante modificare il codice che richiede l'array multidimensionale. – Jodrell

+0

Forse dovresti pubblicare un codice che spieghi esattamente cosa vuoi e spiegare perché lo vuoi in quel modo. – Bernard

risposta

18

Non credo ci sia nulla di costruito nel framework per fare questo - anche in questo caso anche Array.Copy fallisce. Tuttavia, è facile scrivere il codice per farlo loop:

using System; 
using System.Collections.Generic; 

class Test 
{ 
    static void Main() 
    { 
     List<int[]> list = new List<int[]> 
     { 
      new[] { 1, 2, 3 }, 
      new[] { 4, 5, 6 }, 
     }; 

     int[,] array = CreateRectangularArray(list); 
     foreach (int x in array) 
     { 
      Console.WriteLine(x); // 1, 2, 3, 4, 5, 6 
     } 
     Console.WriteLine(array[1, 2]); // 6 
    } 

    static T[,] CreateRectangularArray<T>(IList<T[]> arrays) 
    { 
     // TODO: Validation and special-casing for arrays.Count == 0 
     int minorLength = arrays[0].Length; 
     T[,] ret = new T[arrays.Count, minorLength]; 
     for (int i = 0; i < arrays.Count; i++) 
     { 
      var array = arrays[i]; 
      if (array.Length != minorLength) 
      { 
       throw new ArgumentException 
        ("All arrays must be the same length"); 
      } 
      for (int j = 0; j < minorLength; j++) 
      { 
       ret[i, j] = array[j]; 
      } 
     } 
     return ret; 
    } 

} 
+0

Grazie Jon! Sono un grande fan del tuo libro. Anche se il mio codice sembra simile, il tuo è molto più riutilizzabile. –

2

Se avete intenzione di copiare (non riesco a pensare ad un modo migliore)

var width = ret[0].length; 
var length = ret.Count; 
var newResult = new double[width, length] 
Buffer.BlockCopy(ret.SelectMany(r => r).ToArray(), 
        0, 
        newResult, 
        0, 
        length * width); 
return newResult; 

EDIT

Sono quasi certo che il ciclo anziché utilizzare SelectMany e ToArray è più veloce.

So quando sono stato skeeted.

+0

Ho imparato a conoscere Buffer.BlockCopy. Grazie! –

3

si può fare quanto segue come estensione:

/// <summary> 
    /// Conerts source to 2D array. 
    /// </summary> 
    /// <typeparam name="T"> 
    /// The type of item that must exist in the source. 
    /// </typeparam> 
    /// <param name="source"> 
    /// The source to convert. 
    /// </param> 
    /// <exception cref="ArgumentNullException"> 
    /// Thrown if source is null. 
    /// </exception> 
    /// <returns> 
    /// The 2D array of source items. 
    /// </returns> 
    public static T[,] To2DArray<T>(this IList<IList<T>> source) 
    { 
     if (source == null) 
     { 
      throw new ArgumentNullException("source"); 
     } 

     int max = source.Select(l => l).Max(l => l.Count()); 

     var result = new T[source.Count, max]; 

     for (int i = 0; i < source.Count; i++) 
     { 
      for (int j = 0; j < source[i].Count(); j++) 
      { 
       result[i, j] = source[i][j]; 
      } 
     } 

     return result; 
    } 
+0

Se creo un metodo di estensione per una cosa ampiamente utilizzata, questo rallenterà le mie build, giusto? –

+0

No, è proprio come il metodo normale – Marcin

4

Non c'è un modo semplice per fare questo, perché nella situazione che stai descrivendo, non c'è niente di fermare i double[] array nella lista dall'essere diverse dimensioni, che avrebbe essere incompatibile con una matrice rettangolare bidimensionale. Tuttavia, se siete in grado di garantire i double[] array tutti hanno la stessa dimensionalità, si può creare l'array bidimensionale come segue:

var arr = new double[ret.Count(),ret[0].Count()]; 

for(int i=0; i<ret.Count(); i++) { 
    for(int j=0; j<ret[i].Count(); j++) 
    arr[i,j] = ret[i][j]; 
} 

questo produrrà un errore di run-time se una delle double[] le matrici nella lista sono più corte della prima e perderai i dati se uno degli array è più grande del primo.

Se si è veramente determinati a memorizzare una matrice seghettata in una matrice rettangolare, è possibile utilizzare un valore "magico" per indicare che non vi è alcun valore in quella posizione. Per esempio:

var arr = new double[ret.Count(),ret.Max(x=>x.Count())]; 

for(int i=0; i<ret.Count(); i++) { 
    for(int j=0; j<arr.GetLength(1); j++) 
    arr[i,j] = j<ret[i].Count() ? ret[i][j] : Double.NaN; 
} 

In una nota editoriale, penso che questa sia una pessima idea ™; quando si usa la matrice rettangolare, è necessario verificare Double.NaN in ogni momento. Inoltre, cosa succede se si desidera utilizzare Double.NaN come valore legittimo nell'array? Se hai una matrice seghettata, dovresti lasciarla come una matrice seghettata.

+0

Sì, non c'è nulla che impedisca ai doppi [] array nell'elenco di essere di dimensioni diverse. So che tutti gli array hanno le stesse dimensioni, ma .Net no. –

Problemi correlati