Home > Blockchain >  Is it possible to define function that creates an N-dimensional list of lists easily?
Is it possible to define function that creates an N-dimensional list of lists easily?

Time:02-10

I have searched and found these questions: How to create a multi-dimensional list and N dimensional array in python which hint toward what I am looking for, but they seem to only make 2D arrays and not ND arrays.

Problem:

My problem is that I am able to create an n-dimensional list of lists for a known n, but I am not sure how it can be generalized to work with all values of n.

Consider this example:

def makeList(n):
    return [[[n for _ in range(n)] 
                for _ in range(n)] 
                for _ in range(n)]
print(makeList(3))

Output:

[[[3, 3, 3], 
  [3, 3, 3], 
  [3, 3, 3]], 
 [[3, 3, 3], 
  [3, 3, 3], 
  [3, 3, 3]], 
 [[3, 3, 3], 
  [3, 3, 3], 
  [3, 3, 3]]]

This will create a list of lists that is a 3x3x3 array of 3's which is the intended result, but if we use a different n:

print(makeList(2))

Output:

[[[2, 2], 
  [2, 2]], 
 [[2, 2], 
  [2, 2]]]

This will create a list of lists that is a 2x2x0 array of 2's and this is not the intended result. Instead the results should be a list of lists that is a 2x2 array of 2's.

Likewise if we set n = 4:

print(makeList(4))

This will give a list of lists that is 4x4x4 when it should give a 4x4x4x4 list of lists.

The main issue is that the number of for loops must change depending on the input, but obviously the code can't "come to life" and recode itself magically, hence my issue.

I am looking for a way to get this result that is simple (a few lines of code). I am sure I could continue developing ideas for solutions, but I have not been able to think of anything that is only a couple of lines.

What I have tried:

The first idea I thought of was to use recursion, and this my simple approach:

def makeList(n, output = []):
    if not output:
        output = [n for _ in range(n)]
    else:
        output = [output for _ in range(n)]
    if len(output) == n:
        return output
    else:
        return makeList(n, output)

This obviously will not work because if len(output) == n: will execute the first iteration since the length of the inner loops is equal to the length of the outermost loop. However, even if there was a condition that properly terminated the function, this solution would still not be ideal because I could run into maximum recursion errors with large values of n. Furthermore, even if these issues were resolved this code is still quite long as well as time consuming.

The other potential perspective I considered (and the solution that I found that works) is using a dict. My solution saves the intermediate lists of lists in a dict so it can be used for the next iteration:

def makeList(n):
    d = {str(i): None for i in range(n)}
    for i in range(n):
        if i == 0:
            d[str(i)] = [n for _ in range(n)]
        else:
            d[str(i)] = [d[str(i-1)] for _ in range(n)]
    return d[str(n-1)]

But again, this is very long and doesn't seem very pythonic.

Solution requirements:

The ideal solution would be much more concise than mine with an efficient time complexity that only uses built-in functions.

Other options that are close to these requirements would also be helpful as long as the spirit of the answer is trying to best meet the requirements.

Being concise is the most important aspect, however, which is why I am not fully happy with my current attempts.

CodePudding user response:

Using tuple(n for _ in range(n)) to get the dimension that you want, and numpy to generate a multidimensional array:

import numpy as np

def make_array(n):
    return np.full(tuple(n for _ in range(n)), n)

for n in range(1,5):
    print(make_array(n))

Output:

[1]

[[2 2]
 [2 2]]

[[[3 3 3]
  [3 3 3]
  [3 3 3]]

 [[3 3 3]
  [3 3 3]
  [3 3 3]]

 [[3 3 3]
  [3 3 3]
  [3 3 3]]]

[[[[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]]


 [[[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]]


 [[[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]]


 [[[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]

  [[4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]
   [4 4 4 4]]]]

CodePudding user response:

Your BEST plan is to use numpy for this. You can create an arbitrary array of all zeros with np.zeros( (4,4,4) ), or an array of 1s with np.ones( (4,4,4) ). If you're going to be working with arrays very much at all, you will certainly want to use numpy.

CodePudding user response:

I've sketched out a recursive function that might suit:

def nd_list(n, x=None):
    if x is None:
        x = n
    if x == 1:
        return [n] * n
    return [nd_list(n, x-1)] * n

In two lines using no external libraries!

def nd_list(x, z):
    return [z] * z if x == 1 else [nd_list(x-1, z)] * z

In two lines (just) and without the list of lists surprising behaviour:

def nd_list(x, z):
    return [0 for _ in range(z)] if x == 1 else [nd_list(x-1, z) for _ in range(z)]
  • Related