C#: convert nested list to nd-array

Viewed 110

I want to convert a nested list, with arbitrarily many nesting levels, of type T to a multi dimentional array of type T

This is exactly like this, but with arbitrarily many levels, which would imply recursion.

How can this be done with C#?

My type is for example

List<List<List<List<int>>>>

and would expect either a int[][][][] or a int[,,,], preferably the latter.

  • This is assuming of course every sibling list is of the same size.
1 Answers

Here is a solution for n dimensions. I create a fake list of 4 dimensions for your example with MakeList() The key is first to find the dimensions and their size, which is done in CountDimensions. This array of dimensions allow you to create an array of the size you want with Array.CreateInstance Then I loop through all values by creating an array of indexes that is incremented 1 by 1 and propagate into next dimensions when one is full. The same array of indexes is used to dig into the lists through recursion with GetListValue.

This is probably not optimal for performance but that should give you a start.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;

namespace NestedList
{
    public class ListToArray
    {
        public Array Convert(IList input)
        {
            var dimensions = CountDimensions(input).ToArray();
            var result = Array.CreateInstance(typeof(int), dimensions);

            var indexes = new int[dimensions.Length];
            while (indexes[0] < dimensions[0]) 
            {
                var currentValue = GetListValue(input, indexes, 0);
                result.SetValue(currentValue, indexes);

                // Inc last index, propagate to higher if needed
                IncrementIndex(indexes, dimensions);
            }

            return result;
        }

        private int GetListValue(IList input, int[] indexes, int depth)
        {
            if (input[indexes[depth]] is IList sublist)
            {
                return GetListValue(sublist, indexes, depth + 1);
            }
            return (int)input[indexes[depth]];
        }

        private void IncrementIndex(int[] indexes, int[] dimensions)
        {
            var iIndex = indexes.Length - 1;
            indexes[iIndex] = indexes[iIndex] + 1;
            while (iIndex > 0 && indexes[iIndex] > dimensions[iIndex]-1)
            {
                indexes[iIndex] = 0;
                iIndex--;
                indexes[iIndex] = indexes[iIndex] + 1;
            }
        }

        private IEnumerable<int> CountDimensions(IList input)
        {
            yield return input.Count;
            if (input.Count > 0 && input[0] is IList sublist)
            {
                foreach (var value in CountDimensions(sublist))
                {
                    yield return value;
                }
            }
            yield break;
        }

    }

    public class Program
    {
        static void Main()
        {
            var dec = new ListToArray();
            var array = dec.Convert(MakeList());
        }

        private static List<List<List<List<int>>>> MakeList()
        {
            const int dim1 = 3;
            const int dim2 = 4;
            const int dim3 = 2;
            const int dim4 = 5;

            var nextValue = 0;
            var dim1List = new List<List<List<List<int>>>>();
            for (var x1 = 0; x1 < dim1; ++x1)
            {
                var dim2List = new List<List<List<int>>>();
                for (var x2 = 0; x2 < dim2; ++x2)
                {
                    var dim3List = new List<List<int>>();
                    for (var x3 = 0; x3 < dim3; ++x3)
                    {
                        var dim4List = new List<int>();
                        for (var x4 = 0; x4 < dim4; ++x4)
                        {
                            var element4 = nextValue++;
                            dim4List.Add(element4);
                        }
                        dim3List.Add(dim4List);
                    }
                    dim2List.Add(dim3List);
                }
                dim1List.Add(dim2List);
            }

            return dim1List;
        }
    }
}

Edit: I had left initially a variable for another solution, now removed.

Related