【问题标题】:How to convert list of arrays into a multidimensional array如何将数组列表转换为多维数组
【发布时间】:2012-04-04 05:13:23
【问题描述】:

我需要将下面的集合转换成double[,]:

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

列表中的所有数组都具有相同的长度。最简单的方法ret.ToArray() 会产生 double[][],这不是我想要的。当然,我可以手动创建一个新数组,然后循环复制数字,但有没有更优雅的方法?

编辑:我的库是从另一种语言 Mathematica 调用的,该语言尚未在 .Net 中开发。我不认为该语言可以利用锯齿状数组。我必须返回一个多维数组。

【问题讨论】:

  • 听起来你想要一个 jagged 数组而不是 2D 数组 - 你确定这是你的要求,因为所有数组都具有相同的长度吗?
  • 更改需要多维数组的代码可能会更优雅。
  • 也许你应该发布代码,准确地解释你想要什么,并解释你为什么要这样。
  • 只复制数据有什么问题?归根结底,要更改需要发生的结构,并且双 for 循环还不够复杂,以至于我认为需要花费时间和精力将其更改为其他内容。如果你经常这样做,你可以制作一个实用方法来进行转换。
  • 如果可以的话,我建议将输出保留为锯齿状数组。 What is differences between Multidimensional array and Array of Arrays in C#?

标签: c# linq multidimensional-array jagged-arrays


【解决方案1】:

我不相信框架中内置了任何东西可以做到这一点——即使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;
    }

}

【讨论】:

  • 谢谢乔恩!我是你的书的忠实粉丝。虽然我的代码看起来很相似,但你的代码更可重用。
【解决方案2】:

你可以做以下扩展:

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

【讨论】:

  • 如果我为一个广泛使用的东西创建一个扩展方法,那会减慢我的构建速度,对吧?
  • 不,和普通方法一样
【解决方案3】:

没有简单的方法可以做到这一点,因为在您描述的情况下,没有什么可以阻止列表中的 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 作为数组中的合法值怎么办?如果你有一个锯齿状数组,你应该把它保留为一个锯齿状数组。

【讨论】:

  • 是的,没有什么可以阻止列表中的 double[] 数组大小不同。我知道所有数组的大小都相同,但 .Net 没有。
【解决方案4】:

如果你要复制(我想不出更好的方法)

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;

编辑

我几乎可以肯定循环比使用SelectManyToArray 更快。

我知道我什么时候被骗了。

【讨论】:

    【解决方案5】:

    我正在寻找相反的要求,我需要将二维数组转换为数组数组。我需要这个,因为 json 本身不支持多维数组的序列化。

    /// <summary>
    /// converts a 2 dimensional array to an array of arrays
    /// </summary>
    /// <typeparam name="T">type of arays</typeparam>
    /// <param name="src">2-dimensional array</param>
    /// <returns>array of arrays of the same size the <paramref name="src"/></returns>
    public static T[][] Convert2DArray<T>(this T[,] src)
    {
        // match input
        if (src == null)
            return null;
    
        // get array dimensions
        var height = src.GetLength(0);
        var width = src.GetLength(1);
    
        // create the new array
        var tgt = new T[height][];
        for (int i = 0; i < height; i++)
        {
            tgt[i] = new T[width];
            for (int j = 0; j < width; j++)
                tgt[i][j] = src[i, j];
        }
    
        // return it
        return tgt;
    }
    

    这是一个 3 维版本:

    /// <summary>
    /// converts a 3 dimensional array to an array of arrays
    /// </summary>
    /// <typeparam name="T">type of arays</typeparam>
    /// <param name="src">3-dimensional array</param>
    /// <returns>array of arrays of the same size the <paramref name="src"/></returns>
    public static TDest[][][] Convert3DArray<TSrc, TDest>(this TSrc[,,] src, Func<TSrc, TDest> converter)
    {
        // match input
        if (src == null)
            return null;
        if (converter is null)
            throw new ArgumentNullException(nameof(converter));
    
        // get array dimensions
        var iLen = src.GetLength(0);
        var jLen = src.GetLength(1);
        var kLen = src.GetLength(2);
    
        // create the new array
        var tgt = new TDest[iLen][][];
        for (int i = 0; i < iLen; i++)
        {
            tgt[i] = new TDest[jLen][];
            for (int j = 0; j < jLen; j++)
            {
                tgt[i][j] = new TDest[kLen];
                for (int k = 0; k < kLen; k++)
                    tgt[i][j][k] = converter(src[i, j, k]);
            }
        }
        // return it
        return tgt;
    }
    

    为了更有趣,这个功能可以用来同时移除一个维度(3D->2D)

    /// <summary>
    /// converts a 3 dimensional array to an array of arrays (2D)
    /// </summary>
    /// <typeparam name="T">type of arays</typeparam>
    /// <param name="src">3-dimensional array</param>
    /// <param name="converter">a function to convert the 3rd dimension into a new type (e.g. concatenated string)</param>
    /// <returns>array of arrays of the same size the <paramref name="src"/></returns>
    public static TDest[][] Convert3DTo2DArray<TSrc, TDest>(this TSrc[,,] src, Func<IEnumerable<TSrc>, TDest> converter)
    {
        // match input
        if (src == null)
            return null;
        if (converter is null)
            throw new ArgumentNullException(nameof(converter));
    
        // get array dimensions
        var iLen = src.GetLength(0);
        var jLen = src.GetLength(1);
        var kLen = src.GetLength(2);
    
        // create the new array
        var tgt = new TDest[iLen][];
        for (int i = 0; i < iLen; i++)
        {
            tgt[i] = new TDest[jLen];
            for (int j = 0; j < jLen; j++)
            {
                tgt[i][j] = converter(GetEnumerableFor3rdDimension(src, i, j, kLen));
            }
        }
        // return it
        return tgt;
    }
    
    private static IEnumerable<T> GetEnumerableFor3rdDimension<T>(T[,,] src, int i, int j, int kLen)
    {
        for (int k = 0; k < kLen; k++)
            yield return src[i, j, k];
    }
    

    【讨论】:

      猜你喜欢
      • 2013-05-17
      • 2020-03-24
      • 2015-08-18
      • 2019-07-09
      • 2011-08-24
      • 2019-04-03
      • 1970-01-01
      • 2019-06-25
      • 2021-04-15
      相关资源
      最近更新 更多