Help us understand the problem. What is going on with this article?

2次元配列でLINQしたい

More than 1 year has passed since last update.

2次元配列を扱うコードを書いてたら、行や列に対してLINQが使えないのが微妙にストレスだったんで、簡単な拡張メソッドを書いてみました。

/// <summary>
/// 2次元方向
/// </summary>
public enum SquareDirection { Row = 0, Column = 1 }

/// <summary>
/// 配列の拡張メソッド
/// </summary>
public static class ArrayEx
{
    /// <summary>
    /// 2次元配列向け列クラス
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class SquareArrayColumn<T> : IEnumerable<T>
    {
        /// <summary>
        /// 2次元配列
        /// </summary>
        private T[,] _array;
        /// <summary>
        /// 列インデックス
        /// </summary>
        private int _colIndex;

        /// <summary>
        /// インデクサ
        /// </summary>
        /// <param name="rowIndex">行インデックス</param>
        /// <returns>要素</returns>
        public T this[int rowIndex]
        {
            get => _array[rowIndex, _colIndex];
            set => _array[rowIndex, _colIndex] = value;
        }

        /// <summary>
        /// Length
        /// </summary>
        public int Length => _array.GetLength(0);

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="array">2次元配列</param>
        /// <param name="rowIndex">行インデックス</param>
        public SquareArrayColumn(T[,] array, int colIndex)
        {
            _array = array;
            _colIndex = colIndex;
        }

        /// <summary>
        /// GetEnumerator()の実装
        /// </summary>
        /// <returns>IEnumerator<T></returns>
        public IEnumerator<T> GetEnumerator()
        {
            for (var i = 0; i < Length; i++)
                yield return this[i];
        }

        /// <summary>
        /// GetEnumerator()の実装
        /// </summary>
        /// <returns>IEnumerator</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }

    /// <summary>
    /// 2次元配列向け行クラス
    /// </summary>
    /// <typeparam name="T">型パラメータ</typeparam>
    public class SquareArrayRow<T> : IEnumerable<T>
    {
        /// <summary>
        /// 2次元配列
        /// </summary>
        private T[,] _array;
        /// <summary>
        /// 行インデックス
        /// </summary>
        private int _rowIndex;

        /// <summary>
        /// インデクサ
        /// </summary>
        /// <param name="colIndex">列インデックス</param>
        /// <returns>要素</returns>
        public T this[int colIndex]
        {
            get => _array[_rowIndex, colIndex];
            set => _array[_rowIndex, colIndex] = value;
        }

        /// <summary>
        /// Length
        /// </summary>
        public int Length => _array.GetLength(1);

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="array">2次元配列</param>
        /// <param name="rowIndex">行インデックス</param>
        public SquareArrayRow(T[,] array, int rowIndex)
        {
            _array = array;
            _rowIndex = rowIndex;
        }

        /// <summary>
        /// GetEnumerator()の実装
        /// </summary>
        /// <returns>IEnumerator<T></returns>
        public IEnumerator<T> GetEnumerator()
        {
            for (var i = 0; i < Length; i++)
                yield return this[i];
        }

        /// <summary>
        /// GetEnumerator()の実装
        /// </summary>
        /// <returns>IEnumerator</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }
    }

    /// <summary>
    /// 2次元配列から行を取得する
    /// </summary>
    /// <typeparam name="T">型パラメータ</typeparam>
    /// <param name="array">this 2次元配列</param>
    /// <param name="rowIndex">行インデックス</param>
    /// <returns>行オブジェクト</returns>
    public static SquareArrayRow<T> Rows<T>(this T[,] array, int rowIndex)
    {
        if (rowIndex < 0 || array.GetLength(0) <= rowIndex)
            throw new IndexOutOfRangeException();
       return new SquareArrayRow<T>(array, rowIndex);
    }

    /// <summary>
    /// 2次元配列から行を列挙する
    /// </summary>
    /// <typeparam name="T">型パラメータ</typeparam>
    /// <param name="array">this 2次元配列</param>
    /// <returns>行オブジェクトの列挙子</returns>
    public static IEnumerable<SquareArrayRow<T>> Rows<T>(this T[,] array)
    {
        for (var row = 0; row < array.GetLength(0); row++)
            yield return array.Rows(row);
    }

    /// <summary>
    /// 2次元配列から列を取得する
    /// </summary>
    /// <typeparam name="T">型パラメータ</typeparam>
    /// <param name="array">this 2次元配列</param>
    /// <param name="colIndex">列インデックス</param>
    /// <returns>行オブジェクト</returns>
    public static SquareArrayColumn<T> Cols<T>(this T[,] array, int colIndex)
    {
        if (colIndex < 0 || array.GetLength(1) <= colIndex)
            throw new IndexOutOfRangeException();
        return new SquareArrayColumn<T>(array, colIndex);
    }

    /// <summary>
    /// 2次元配列から列を列挙する
    /// </summary>
    /// <typeparam name="T">型パラメータ</typeparam>
    /// <param name="array">this 2次元配列</param>
    /// <returns>行オブジェクトの列挙子</returns>
    public static IEnumerable<SquareArrayColumn<T>> Cols<T>(this T[,] array)
    {
        for (var col = 0; col < array.GetLength(1); col++)
            yield return array.Cols(col);
    }

    /// <summary>
    /// 2次元配列をまとめて列挙する
    /// </summary>
    /// <typeparam name="T">型パラメータ</typeparam>
    /// <param name="array">2次元配列</param>
    /// <param name="direction">方向</param>
    /// <returns>列挙子</returns>
    public static IEnumerable<T> Flatten<T>(this T[,] array, SquareDirection direction = SquareDirection.Row)
    {
        IEnumerable<T> rowDirection()
        {
            for (var row = 0; row < array.GetLength(0); row++)
            {
                for (var col = 0; col < array.GetLength(1); col++)
                {
                    yield return array[row, col];
                }
            }
        }
        IEnumerable<T> colDirection()
        {
            for (var col = 0; col < array.GetLength(1); col++)
            {
                for (var row = 0; row < array.GetLength(0); row++)
                {
                    yield return array[row, col];
                }
            }
        }

        if (direction == SquareDirection.Row)
            return rowDirection();
        else
            return colDirection();
    }
}

使い方は、拡張メソッドなので任意の2次元配列 (array<T>[行,列]) に対して、

array.Rows()

で行単位での Enumerator、

array.Cols()

で列単位の Enumerator を返します。
例えば、2次元配列の行単位の合計を取得するには

var array = new int[,]
       {
            { 1, 2, 3 },
            { 4, 5, 6 },
            { 7, 8, 9 }
        };

// 行毎の合計を撮る
var sums = array.Rows.Select(r => r.Sum()).ToArray(); // => [ 6, 15, 24 ]

という感じ。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away