C#で1次元配列と2次元配列を相互に変換する

タイトルの通り、1次元配列と2次元配列の相互変換を行う処理の紹介です。

考え方

このような2次元配列を

f:id:Takachan:20200229161039p:plain

このような配列に変換することができます。

f:id:Takachan:20200229164959p:plain

実装コード

早速実装例を紹介したいと思います。

ArrayUtilityクラス

配列に対する操作のため以前紹介したArrayUtiityクラスを今回も使用します。

// ArrayUtility.cs

using System;
using System.Collections.Generic;

/// <summary>
/// 配列に対する汎用機能を提供します。
/// </summary>
public static class ArrayUtility
{
    /// <summary>
    /// 指定した2次元配列を1次元配列に変換します。
    /// </summary>
    public static T[] ToOneDimensional<T>(T[,] src)
    {
        int ymax = src.GetLength(0);
        int xmax = src.GetLength(1);
        int len = xmax * ymax;
        var dest = new T[len];

        for (int y = 0, i = 0; y < ymax; y++)
        {
            for (int x = 0; x < xmax; x++, i++)
            {
                dest[i] = src[y, x];
            }
        }
        return dest;
    }

    /// <summary>
    /// 組み込み型のみを対象に2次元配列を1次元配列に変換します。
    /// </summary>
    public static T[] ToOneDimensionalPrimitives<T>(T[,] src)
    {
        int ymax = src.GetLength(0);
        int xmax = src.GetLength(1);
        int len = xmax * ymax;
        var dest = new T[len];

        var size = Marshal.SizeOf(typeof(T));
        Buffer.BlockCopy(src, 0, dest, 0, len * size);
        return dest;
    }

    /// <summary>
    /// 指定した2次元配列を1次元配列に変換します。
    /// <para>T[height, width] 範囲を超える分は切り捨て、不足している分は(T)の初期値になります。</para>
    /// </summary>
    public static T[,] ToTowDimensional<T>(T[] src, int width, int heigth)
    {
        var dest = new T[heigth, width];
        int len = width * heigth;
        len = src.Length < len ? src.Length : len;
        for (int y = 0, i = 0; y < heigth; y++)
        {
            for (int x = 0; x < width; x++, i++)
            {
                if (i >= len)
                {
                    return dest;
                }
                dest[y, x] = src[i];
            }
        }

        return dest;
    }

    /// <summary>
    ///  組み込み型のみを対象に1次元配列を2次元配列に変換します。
    /// <para>T[height, width] 範囲を超える分は切り捨て、不足している分は(T)の初期値になります。</para>
    /// </summary>
    public static T[,] ToTowDimensionalPrimitives<T>(T[] src, int width, int heigth)
    {
        var dest = new T[heigth, width];
        int len = width * heigth;
        len = src.Length < len ? src.Length : len;

        var size = Marshal.SizeOf(typeof(T));
        Buffer.BlockCopy(src, 0, dest, 0, len * size);
        return dest;
    }
}

使い方

使い方はシンプルに変換したい配列を指定し、X, Y のサイズを指定し変換を行います。

// AppMain.cs

using System;
using System.Collections.Generic;

public static void Main(string[] args)
{
    // 1次元配列を宣言
    int[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

    // 2x2 の 2次元配列に変換、はみ出した分は無視される
    var tow = ArrayUtility.ToTowDimensional(array, 2);
    // 0, 1
    // 2, 3

    // 2次元配列を1次元配列に変換
    var one = ArrayUtility.ToOneDimensional(array);
    // 0, 1, 2, 3

    Console.WriteLine(one);
}

ArrayExtension:拡張メソッド版

上記の処理を配列の拡張メソッドとして定義したいと思います。

コード例は以下の通りです。

先ほどのUtilityの処理を中で呼び出すようにしています。

/// <summary>
/// 配列に対する拡張機能を提供します。
/// </summary>
public static class ArrayExtension
{
    /// <summary>
    /// 指定した2次元配列を1次元配列に変換します。
    /// </summary>
    public static T[] ToOneDimensional<T>(this T[,] array)
    {
        ArrayUtility.ToOneDimensional(array);
    }

    /// <summary>
    /// 指定した2次元配列を1次元配列に変換します。
    /// </summary>
    public static T[,] ToTowDimensional<T>(this T[] array, int size)
    {
        ArrayUtility.ToTowDimensional(array, size);
    }
}

使い方

使用方法は先ほどとほぼ同じですが配列のインスタンスを直接指定して処理を呼び出せるようになっていると思います。

// AppMain.cs

using System;
using System.Collections.Generic;

public static void Main(string[] args)
{
    // 1次元配列を宣言
    int[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };

    // 2x2 の 2次元配列に変換、はみ出した分は無視される
    var tow = array.ToTowDimensional(2);
    // 0, 1
    // 2, 3

    // 2次元配列を1次元配列に変換
    var one = array.ToOneDimensional();
    // 0, 1, 2, 3

    Console.WriteLine(one);
}

こちらも最初のコードと同じように相互変換することができました。

コードばかりになってしまいましたが以上です。