2012-03-19 1 views
11

нужно преобразовать следующую коллекцию в двойной [,]:Как преобразовать список массивов в многомерный массив

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

Все массивы в списке имеют одинаковую длину. Простейший подход, ret.ToArray(), производит double [] [], что не то, что я хочу. Конечно, я могу создать новый массив вручную и скопировать числа в цикле, но есть ли более элегантный способ?

Редактировать: Моя библиотека вызывается с другого языка, Mathematica, который не был разработан в .Net. Я не думаю, что язык может использовать зубчатые массивы. Мне нужно вернуть многомерный массив.

+1

звучит так, будто вы хотите, чтобы массив * jagged * не был двумерным массивом - вы уверены, что это ваше требование, поскольку все массивы имеют одинаковую длину? – BrokenGlass

+1

, вероятно, было бы более элегантно изменять код, который нуждается в многомерном массиве. – Jodrell

+0

Возможно, вам следует опубликовать код, который объясняет, что именно вы хотите, и объяснить, почему вы так хотите. – Bernard

ответ

18

Я не верю, что в каркас есть что-то, что можно было бы сделать - даже Array.Copy в этом случае не работает. Тем не менее, это легко написать код, чтобы сделать это с помощью цикла:

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

Спасибо, Джон! Я большой поклонник вашей книги. Хотя мой код выглядит похожим, ваш способ более многоразовый. –

2

Если вы собираетесь копировать (я не могу придумать лучшего способа)

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

Я почти уверен, что вместо того, чтобы использовать SelectMany и ToArray, это скорее.

Я знаю, когда я был на краю.

+0

Я узнал о Buffer.BlockCopy. Благодаря! –

3

Вы можете сделать следующее в качестве расширения:

/// <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

Если я создаю метод расширения для широко используемой вещи, это замедлит мои сборки, верно? –

+0

Нет, это как обычный метод – Marcin

4

Там нет простого способа сделать это, потому что в ситуации, вы описываете, нет ничего останавливая double[] массивы в списке от того, разных размеров, которые бы быть несовместимым с двумерным прямоугольным массивом. Однако, если вы в состоянии гарантировать double[] массивы все имеют ту же размерность, вы можете построить свой двумерный массив следующим образом:

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

Это приведет к ошибке во время выполнения, если какой-либо из double[] массивы в списке короче первого, и вы потеряете данные, если какой-либо из массивов больше первого.

Если вы действительно решили хранить зубчатый массив в прямоугольном массиве, вы можете использовать «магическое» значение, чтобы указать, что в этой позиции нет значения. Например:

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

На редакционном примечании, я думаю, что это очень плохая идея ™; когда вы идете использовать прямоугольный массив, вы должны постоянно проверять на наличие Double.NaN. Кроме того, что, если вы хотите использовать Double.NaN как законное значение в массиве? Если у вас есть зубчатый массив, вы должны просто оставить его как зубчатый массив.

+0

Да, нет ничего, что останавливало бы двойные [] массивы в списке из разных размеров. Я знаю, что все массивы имеют одинаковые размеры, но .Net нет. –