1

I have a matrix which is given in a list of lists:

board = [[1, 0, 0, 1], [-1, -1, 1, 1], [0, 0, 0, 0]]

What I want to do is change the board but without copying. I want to change it into a columns board:

def foo(board):

    change board to columns board

board = foo(board)

This is the result:

board = [[1, -1, 0], [0, -1, 0], [0, 1, 0], [1, 1, 0]]

Problem is it creates a new variable with the same name (board) and doesn't change the original board.

I need to change the original board as I need to use it in different places afterward.

Cannot return the board, as I would've gladly done without thinking about it (not allowed).

Even tried:

a = foo(board)
board.clear()
board += a

Which should work but for some reason bugs my program. any help would be appreciated

Bernardo Duarte
  • 4,074
  • 4
  • 19
  • 34
pipmich
  • 13
  • 2
  • You can do `board[:] = foo(board)` <- this will change values inside the `board` variable – Andrej Kesely Dec 20 '19 at 17:19
  • what do you mean by "for some reason bugs my program"? what error are you getting? – Personman Dec 20 '19 at 17:22
  • 2
    While it is possible to modify the list from inside the `foo()` function, this is likely not the best solution to your original problem. Such side effects lead to difficult-to-find bugs because values are changed unexpectedly. Can you provide more context about what you are trying to do here? – Code-Apprentice Dec 20 '19 at 17:23
  • 1
    @AndrejKesely board[:] works great, thanks! actually tried a different way which worked too, but this one's better – pipmich Dec 20 '19 at 18:55
  • @Personman seems i had another similar problem which is why it bugged my program, no error received, just wrong outcome – pipmich Dec 20 '19 at 18:56
  • @Code-Apprentice yea i know it seems a bit unnatural, some school work requirements so had to do it that way. need the board itself to update in the function and then use it in another one – pipmich Dec 20 '19 at 18:59

3 Answers3

1

You could do it in-place, but since it changes the entire structure of the board (number of sublists etc.) it does IMHO not make much sense and seems a bit unnatural. If the only problem is that you have other references pointing to the same board, you could assign to the slice board[:] to overwrite all the contents of board with the new board, without assigning a new value to board itself.

def swap(b):
    return list(map(list, zip(*b)))

>>> board = [[1, 0, 0, 1], [-1, -1, 1, 1], [0, 0, 0, 0]]
>>> also_board = board
>>> board[:] = swap(board)
>>> also_board
[[1, -1, 0], [0, -1, 0], [0, 1, 0], [1, 1, 0]]

This is not in-place in a memory-point-of-view, but for your purpose it should work.

tobias_k
  • 81,265
  • 12
  • 120
  • 179
0

That is called a transposition. You can achieve this by spreading the 2D list into zip and converting each item to a list:

board = [[1, 0, 0, 1], [-1, -1, 1, 1], [0, 0, 0, 0]]
bid = id(board)
print(board)
# The posed question insists on mutating board so here it is.
for i, item in enumerate(list(item) for item in zip(*board)):
    try:
        board[i] = item
    except IndexError:
        board.append(item)

print(bid == id(board))
print(board)

Output:

[[1, 0, 0, 1], [-1, -1, 1, 1], [0, 0, 0, 0]]
True
[[1, -1, 0], [0, -1, 0], [0, 1, 0], [1, 1, 0]]

See this answer.

dmmfll
  • 2,666
  • 2
  • 35
  • 41
  • Edited. Same board. Updated. More code. Now the board has been mutated rather than replaced. The code with mutation takes over twice as long as replacing the board with `board = [list(item) for item in zip(*board)]` which is the comprehensive equivalent to `list(map(list, zip(*b)))` in the accepted answer. Checked with timeit in Jupyter notebook: 12.2 µs ± 47 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) vs 5.89 µs ± 23.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) – dmmfll Dec 21 '19 at 20:19
  • `board[:]` creates a new board in the accepted answer. It has a different id. – dmmfll Dec 21 '19 at 20:29
  • The above comments are comments in response to a deleted comment that the answer that was there `board = [list(item) for item in zip(*board)]` doesn't update the existing items in board. – dmmfll Dec 21 '19 at 20:32
0

Just for fun

def transpose(matrix):
    rows = len(matrix)
    if rows < 1:
        raise ValueError("matrix has to have at least one row")
    rowlen = len(matrix[0])
    if rowlen == 0:
        raise ValueError("rows must not be empty")
    if rowlen == 1 and rows == 1:
        return
    for i in range(1, rows):
        if len(matrix[i]) != rowlen:
            raise ValueError("rows must have the same lenght")
    index = 0
    while index < rowlen:
        newrow = []
        for c in range(rows):
            newrow.append(matrix[c].pop(0))
        matrix.append(newrow)
        index += 1
        if index == rowlen:
            while(len(matrix[0]) == 0):
                matrix.pop(0)


board = [[1,0,0,1], [-1,-1,1,1], [0,0,0,0]]
print("id|{}: {}".format(id(board), board))
print("transposing...")
transpose(board)
print("id|{}: {}".format(id(board), board))

Output

id|140328172362568: [[1, 0, 0, 1], [-1, -1, 1, 1], [0, 0, 0, 0]]

transposing...

id|140328172362568: [[1, -1, 0], [0, -1, 0], [0, 1, 0], [1, 1, 0]]

It's very slow, better stick with zip

Arthur Grigoryan
  • 358
  • 3
  • 12