views:

145

answers:

3

I've got a list of 400 numbers, and i want to but them in a 20x20 grid using Python.

I've made a "2d array" (not really because Python doesn't support them, I've had to use a list of lists.)

When i try to loop through and assign each subsequnt item to the next box in the grid, it fails. i end up assinging the last item in the list to every box.

Here's the code:

numbers = "08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08 49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00 81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65 52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91 22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80 24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50 32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70 67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21 24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72 21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95 78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92 16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57 86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58 19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40 04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66 88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69 04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36 20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16 20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54 01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48"
grid = [[0 for col in range(20)] for row in range(20)]

for x in range(0, 1200, 3):
    y = x + 2
    a = numbers[x:y]
    for i in range(20):
        for j in range(20):
            grid[i][j] = a

print(grid)

I can see why it's going wrong: the two loops that generate the list coordinates are inside the loop that gets each items from the list, so each time they run the value they are assigning doesn't change.

Therefore I guess seeing as they don't work in the loop, they need to be out of it.

The problem is that I can't work out where exactly.

Anyone give me a hand?

+6  A: 

This is the sort of thing that list comprehensions are good for.

nums = iter(numbers.split())
grid = [[next(nums) for col in range(20)] for row in range(20)]

Alternatively, as a for loop:

grid = [[0]*20 for row in range(20)]
nums = iter(numbers.split())
for i in xrange(20):
    for j in xrange(20):
        grid[i][j] = next(nums)

I'm not recommending that you do this, but the way to do it if you don't just want to split the list and then call next on its iterator is to write a generator to parse the list the way that you were and then call next on that. I point this out because there are situations where builtins wont do it for you so you should see the pattern (not Design Pattern, just pattern for the pedantic):

def items(numbers):
    for x in range(0, len(numbers), 3):
        yield numbers[x:x+2]

 nums = items(numbers)
 for i in xrange(20):
     for j in xrange(20):
         grid[i][j] = next(nums)

This lets you step through the two loops in parallel.

aaronasterling
The top one is neat :)
jkp
Yeah, top one worked great and is very concise. I'll look into the documentation for next and iter, as they look very handy and i want to know how it works for the future.
@user464154, yeah, they're useful tools. Also, I wouldn't be the least bit offended if you wanted to accept unutbu's answer instead of mine. It's nice and dense.
aaronasterling
To be honest, I think it can be written a little more nicely with slices: grid = [numbers[i:i+20] for i in range(0, 400, 20)]
James Cunningham
@James Cunningham, you should put that as an answer.
aaronasterling
+5  A: 

Another alternative is to use the grouper idiom:

nums = iter(numbers.split())
grid = zip(*[nums]*20)

Note that this makes a list of tuples, not a list of lists. If you need a list of lists, then

grid = map(list,zip(*[nums]*20))
unutbu
I'm never going to forget about this one again. Thanks.
aaronasterling
+2  A: 

Your for loop confusion stems from having more loops than you need.

Instead, one approach is to start by looping over your grid squares and then figuring out the needed offset into your list of numbers:

for i in range(20):
  for j in range(20):
    offset = i*20*3 + j*3
    grid[i][j] = numbers[offset:offset+2]

In this case, the offset has a few constants. i is the row, so for each row you need to skip over a row's worth of characters (60) in your list. j is the column index; each column is 3 characters wide.

You could, of course, do this operation in reverse. In that case, you would loop over your list of numbers and then figure out to which grid square it belongs. It works very similarly, except you'd be using division and modulo arithmetic instead of multiplication in the offset above.

Hopefully this provides some insight into how to use for loops to work with multiple objects and multiple dimensions.

lantius
This answer would be correct in C
aaronasterling