1

The code is supposed to make a copy of the original array and rotate it (turn the columns into rows) while keeping the original array the same but the original array changes without a reason.

This is most likely a deep copy issue but I have tried using copy.copy() and it has not worked

Code:

l = [[1,2,3],[4,5,6],[7,8,9]]

def rotate(funcl):
    funcl = funcl[::-1]
    
    for s1 in range(0, len(funcl)):
        for s2 in range(s1, len(funcl)):
            funcl[s1][s2], funcl[s2][s1] = funcl[s2][s1], funcl[s1][s2]

    return funcl

print("Original Array:\n", l)
nextl = rotate(l)
print("Original Array after Function:\n", l)
print("New Rotated Array:\n", nextl)

Output:

Original Array:
 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Original Array after Function:
 [[9, 6, 3], [8, 5, 2], [7, 4, 1]]
New Rotated Array:
 [[7, 4, 1], [8, 5, 2], [9, 6, 3]]

As you can see the function works fine but changes the original array

4
  • 1
    When you pass lists as parameters the original list gets changed (have a look at mutable and immutable objects). You'd need to make a copy of the original list (l.copy()). Commented Jan 4, 2022 at 23:24
  • What you're describing is matrix transposing, have you taken a look at the numpy library? Commented Jan 4, 2022 at 23:26
  • @Einliterflasche Sorry I am not understanding do you mean this: nextl = rotate(l.copy()) Commented Jan 4, 2022 at 23:28
  • @Yash No, in the function, or pass a copy as an argument (have a look at the answers) Commented Jan 4, 2022 at 23:34

4 Answers 4

2

One way is to do a deepcopy with copy.deepcopy() before passing it to the function so every levels and nested sublevels of your list are duplicated:

import copy

l = [[1,2,3],[4,5,6],[7,8,9]]

def rotate(funcl):
    funcl = funcl[::-1]
    
    for s1 in range(0, len(funcl)):
        for s2 in range(s1, len(funcl)):
            funcl[s1][s2], funcl[s2][s1] = funcl[s2][s1], funcl[s1][s2]

    return funcl

print("Original Array:\n", l)
nextl = rotate(copy.deepcopy(l))
print("Original Array after Function:\n", l)
print("New Rotated Array:\n", nextl)

output is:

Original Array:
 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Original Array after Function:
 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
New Rotated Array:
 [[7, 4, 1], [8, 5, 2], [9, 6, 3]]
Sign up to request clarification or add additional context in comments.

Comments

1

deepcopy is notoriously slow and should be avoided if you can.

zip() is frequently used to transpose lists in python. Here the only wrinkle is that you want to reverse them. You can use reversed() for this and get a very succinct:

l = [[1,2,3],[4,5,6],[7,8,9]]

def rotate(funcl):
      return list(zip(*reversed(funcl)))

print("Original Array:\n", l)
nextl = rotate(l)
print("Original Array after Function:\n", l)
print("New Rotated Array:\n", nextl)

Which prints:

Original Array:
 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Original Array after Function:
 [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
New Rotated Array:
 [(7, 4, 1), (8, 5, 2), (9, 6, 3)]

If it's important to maintain inner lists rather than tuples, you can add a map to mix:

l = [[1,2,3],[4,5,6],[7,8,9]]

def rotate(funcl):
      return list(map(list, zip(*reversed(funcl))))
      # or return [list(t) for t in zip(*reversed(funcl))]

rotate(l)
# [[7, 4, 1], [8, 5, 2], [9, 6, 3]]

4 Comments

Is it possible to have lists inside instead of tuples ?
@Malo, yes, I added an edit for that.
@Mark I am confused on how the * works
@Yash it spreads the lists out as individual arguments to zip like calling zip([7,8,9], [4,5,6], [1,2,3]) with three arguments.
1

Here's a version that works (plus a nicer way to print these lists):

def printlst(arr):
    for row in arr:
        print(*row)

def rotate(funcl):
    newl = [row.copy() for row in funcl]
    newl = newl[::-1]
    
    for s1 in range(0, len(newl)):
        for s2 in range(s1, len(newl)):
            newl[s1][s2], newl[s2][s1] = newl[s2][s1], newl[s1][s2]

    return newl


l = [[1,2,3],[4,5,6],[7,8,9]]

print("Original Array:")
printlst(l)
nextl = rotate(l)
print("Original Array after Function:")
printlst(l)
print("New Rotated Array:")
printlst(nextl)

The resulting output:

Original Array:
1 2 3
4 5 6
7 8 9
Original Array after Function:
1 2 3
4 5 6
7 8 9
New Rotated Array:
7 4 1
8 5 2
9 6 3

An alternative approach:

def rotate(funcl):
    return [[funcl[j][i] for j in range(len(funcl))[::-1]] for i in range(len(funcl))]

6 Comments

For what it's worth this almost 5x slower than using zip().
@Mark I was looking to make only a minimal change to the original function. I'm curious about how using zip() compares to Serge's approach; that's probably what I would have done
It also fails on a list like [[1,2,3],[4,5,6],[7,8,9],[10,11,12]]. See zip answer below.
It also only handles only 1 level of nested lists. Otherwise use copy.deepcopy if needed.
@Mark Oh, I meant the modification along the lines of my comment. So the method to compare would be ` def rotate(funcl): \n return [[funcl[j][i] for j in range(len(funcl))[::-1]] for i in range(len(funcl))]`
|
0

This is indeed a deep copy issue: funcl = funcl[::-1] only does a shallow copy.

Anyway the Pythonic way would be to directly build the tranposed array with a double list comprehension avoiding a copy followed with a rotation :

def rotate(funcl):
   return [[funcl[j][i] for j in range(len(funcl))] for i in range(len(funcl))]

1 Comment

You've reflected the array instead of rotating. You should instead have something like return [[funcl[j][i] for j in range(len(funcl))[::-1]] for i in range(len(funcl))].

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.