fahadhub
fahadhub

Reputation: 225

How to change only the diagonal elements of a 2D list?

So I am trying to create an NxN 2D array and then change its diagonal elemets to 1. Here is my code:

arr=[1,1,1,2,2,2]
table=[[0]*len(arr)]*len(arr)
for i in range(0,len(arr)):
    table[i][i]=1
print(table)

However, whenever I run this code, I get this output:

[[1, 1, 1, 1, 1, 1],
 [1, 1, 1, 1, 1, 1],
 [1, 1, 1, 1, 1, 1],
 [1, 1, 1, 1, 1, 1],
 [1, 1, 1, 1, 1, 1],
 [1, 1, 1, 1, 1, 1]]

I am looking to get this:

[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 1, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]

I have been staring at my code for hours and I cannot figure out what's wrong

Upvotes: 2

Views: 722

Answers (4)

Lau Real
Lau Real

Reputation: 321

Interesting. Try to use numpy to avoid list trap:

import numpy as np
org_row = [0]*5
l = [org_row]*5
x = np.array(l, np.int32)
for i in range(len(x)):
    x[i][i]=1
print(x)

output>:

output>
[[1 0 0 0 0]
[0 1 0 0 0]
[0 0 1 0 0]
[0 0 0 1 0]
[0 0 0 0 1]]

Upvotes: 2

mhawke
mhawke

Reputation: 87084

Your 2D "array" contains 6 lists which are the same list. Changes to any of those lists will also be reflected in the other lists. Consider this:

>>> l = [0] * 6
>>> x = [l]
>>> l[0] = 1
>>> l
[1, 0, 0, 0, 0, 0]
>>> x
[[1, 0, 0, 0, 0, 0]]
>>> x = [l, l, l]
>>> x
[[1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0]]
>>> x[-1][-1] = 100
>>> x
[[1, 0, 0, 0, 0, 100], [1, 0, 0, 0, 0, 100], [1, 0, 0, 0, 0, 100]]

This is because the list x contains the list l, so any changes to l are also seen through the reference to the same list in x.

The problem is when multiplying mutable objects because it creates multiple references to the same mutable object.

You should initialise your table like this:

table = [[0 for j in range(len(arr))] for i in range(len(arr))]

or

table = [[0] * len(arr) for i in range(len(arr))]

which, despite the use of multiplication, works because each list is different.

Upvotes: 3

pakpe
pakpe

Reputation: 5479

You can create your table and populate it simultaneously in nested loops:

arr=[1,1,1,2,2,2]

table = []
for i in range(len(arr)):
    table.append([0]*len(arr))
    for j in range(len(arr)):
        if i == j:
            table[i][j] = 1

print(table)
#[[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1]]

Upvotes: 2

Jacob Lee
Jacob Lee

Reputation: 4690

The interesting thing about this is that you are really only editing one list in the for loop, but there are just five pointers to that list. (In this case, the list would be [0, 0, 0, 0, 0, 0].) You can see this by printing the id of each list in table by using id():

>>> for t in table:
        print(id(t))

2236544254464
2236544254464
2236544254464
2236544254464
2236544254464
2236544254464

Your numbers are likely different than mine, but they are all the same number, nevertheless. You also can see that the edits to one list are applied to the others in table by putting a print(table) statement after each index assignment statement.

So in order to 'fix' this, I would recommend using list comprehension instead. For example:

table = [[0]*len(arr) for _ in range(len(arr))]

If you checkout the ids of each list:

>>> for t in table:
    print(id(t))

    
2236544617664
2236544616064
2236544616320
2236544615872
2236544618368
2236544622720

Since they are different, you can now use the method for changing only the diagonals:

>>> for i in range(0,len(arr)):
        table[i][i]=1

>>> table
[[1, 0, 0, 0, 0, 0],
 [0, 1, 0, 0, 0, 0],
 [0, 0, 1, 0, 0, 0],
 [0, 0, 0, 1, 0, 0],
 [0, 0, 0, 0, 1, 0],
 [0, 0, 0, 0, 0, 1]]

Upvotes: 3

Related Questions