web-dev-qa-db-ja.com

配列のリストを多次元配列に変換する方法

次のコレクションをdouble [、]に変換する必要があります。

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

リスト内のすべての配列の長さは同じです。最も単純なアプローチであるret.ToArray()は、double [] []を生成しますが、これは私が望んでいることではありません。もちろん、新しい配列を手動で作成し、ループで数値をコピーすることはできますが、よりエレガントな方法はありますか?

編集:私のライブラリは、.Netで開発されていない別の言語であるMathematicaから呼び出されます。言語がギザギザの配列を利用できるとは思いません。多次元配列を返す必要があります。

17
Arne Lund

これを行うためにフレームワークに組み込まれているものはないと思います。この場合、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;
    }

}
25
Jon Skeet

拡張機能として次の操作を実行できます。

    /// <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;
    }
5
Marcin

あなたが説明している状況では、リスト内のdouble[]配列が異なるサイズになるのを妨げるものは何もないため、これを行う簡単な方法はありません。これは、2次元の長方形配列と互換性がありません。ただし、double[]配列がすべて同じ次元であることを保証する立場にある場合は、次のように2次元配列を作成できます。

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を使用したい場合はどうなりますか?ギザギザの配列がある場合は、ギザギザの配列のままにしておく必要があります。

5
Ethan Brown

コピーする場合(これ以上の方法は考えられません)

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;

[〜#〜]編集[〜#〜]

SelectManyを使用するよりもループする方がほぼ確実です。ToArrayの方が高速です。

私はいつスケベされたか知っています。

4
Jodrell