C # разбивает двумерный массив на партии - PullRequest
3 голосов
/ 29 марта 2019

У меня есть двумерный массив object[,], который содержит матрицу строк и столбцов (object[nRows, nColumns]).

Я бы хотел разделить это на группы строк - например, партии по 1000 строк, каждая из которых я могу перечислить.

Итак, я ищу код C #, который выполняет следующее, но для двумерных массивов ( source ):

private IEnumerable<T[]> SplitArray<T>(T[] sourceArray, int rangeLength)
{
    int startIndex = 0;

    do
    {
        T[] range = new T[Math.Min(rangeLength, sourceArray.Length - startIndex)];
        Array.Copy(sourceArray, startIndex, range, 0, range.Length);
        startIndex += rangeLength;
        yield return range;
    }
    while (startIndex < sourceArray.Length);            
}

Эта попытка адаптации кода для массивов [,] не удалась - строки / столбцы начинают перемешиваться после первой итерации:

        private IEnumerable<T[,]> SplitArray<T>(T[,] sourceArray, int rangeLength)
        {
            int startIndex = 0;

            do
            {
                T[,] range = new T[Math.Min(rangeLength, sourceArray.GetLength(0) - startIndex), sourceArray.GetLength(1)];
                Array.Copy(sourceArray, startIndex, range, 0, range.Length);
                startIndex += rangeLength;
                yield return range;
            }
            while (startIndex < sourceArray.GetLength(0));
        }

Ответы [ 3 ]

2 голосов
/ 29 марта 2019

Это решит ваши проблемы с кодом. Поскольку Array.Copy угрожает массиву как одномерному, вы должны умножиться на количество столбцов, чтобы получить общее количество элементов в некоторых местах:

private IEnumerable<T[,]> SplitArray<T>(T[,] sourceArray, int rangeLength)
{
    int startIndex = 0;
    do
    {
        T[,] range = new T[Math.Min(rangeLength, sourceArray.GetLength(0) - startIndex/sourceArray.GetLength(1)), sourceArray.GetLength(1)];
        Array.Copy(sourceArray, startIndex, range, 0, range.Length);
        startIndex += rangeLength*sourceArray.GetLength(1);
        yield return range;
    }
    while (startIndex < sourceArray.Length);
}
1 голос
/ 29 марта 2019

Я думаю, вы ищете что-то вроде этого:

private static List<T[]> SplitArray<T>(T[,] sourceArray)
{
    List<T[]> result = new List<T[]>();
    int rowCount = sourceArray.GetLength(0);
    for (int i = 0; i < rowCount; i++)
    {
        result.Add(GetRow(sourceArray, i));
    }

    return result;
}

private static T[] GetRow<T>(T[,] sourceArray, int rownumber)
{
    int columnCount = sourceArray.GetLength(1);
    var row = new T[columnCount];
    for (int i = 0; i < columnCount; i++)
    {
        row[i] = sourceArray[rownumber, i];
    }
    return row;
}
1 голос
/ 29 марта 2019

Используя GetLength (целое измерение) , вы можете увидеть, как долго конкретное измерение относится к массиву, а затем выполнить итерацию по нему. Вам также нужно будет взять другие измерения в качестве констант и убедиться, что все это соответствует значению Array.Rank . Оттуда просто посмотрите значение через Array.GetValue (int []) . Это может быть сложно, так как массив не является универсальным:

public static IEnumerable<T> GetRow<T>(this Array source, int dimension, params int[] fixedDimensions)
{
    if(source == null) throw new ArgumentNullException(nameof(source));
    if(!typeof(T).IsAssignableFrom(source.GetType().GetElementType()) throw new OperationException($"Cannot return row of type {typeof(T)} from array of type {source.GetType().GetElementType()}");

    if(fixedDimensions == null) fixedDimensions = new T[0];
    if(source.Rank != fixedDimensions.Length + 1) throw new ArgumentException("Fixed dimensions must have exactly one fewer elements than dimensions in source", nameof(fixedDimensions));
    if(dimension > source.Rank) throw new ArgumentException($"Cannot take dimension {dimension} of an array with {source.Rank} dimensions!", nameof(dimension));
    if(dimension < 0) throw new ArgumentException("Cannot take a negative dimension", nameof(dimension));

    var coords = dimension == source.Rank
         ? fixedDimensions
            .Concat(new [] { 0 })
            .ToArray()
        : fixedDimensions
            .Take(dimension)
            .Concat(new [] { 0 })
            .Concat(fixedDimensions.Skip(dimension))
            .ToArray();

    var length = source.GetLength(dimension);
    for(; coords[dimension] < length; coords[dimension]++)
    {
        yield return (T)source.GetValue(coords);
    }
}
...