-1

I'm fairly new to programming and I'm really stuck in a problem.

Say I have the following list:

a = [2, "**", 2, "@"]

The list might contain more elements.

What I need to do is change positions between "**" and "@", so I would have

a = [2, "@", 2, "**"]

I've been trying to do it with a for loop, using element indexes to perform the change, but the list index is getting out of range.

How could I do it?

Here's my code:

for j in range (len(expression)):
    if expression[j] == "**":
        if expression[j+2] == "@":
            expression[j], expression[j+2] = expression[j+2], expression[j]
print(expression)
  • 2
    Please add your current code to the question – Abhinav Mathur Oct 18 '20 at 10:49
  • 1
    Does this answer your question? [How to switch position of two items in a Python list?](https://stackoverflow.com/questions/2493920/how-to-switch-position-of-two-items-in-a-python-list) – awesoon Oct 18 '20 at 10:53
  • 1
    Do you want to change _all_ `@`s with `**`s and the reverse? or only their first occurrence? – urban Oct 18 '20 at 10:54
  • I need to change if and only if ** and @ are separated by another element. There's a specific order. Say **, x, @ – brazilian_student Oct 18 '20 at 10:56
  • Your code seems ok BUT you have to check that j+2 exists to avoid the index error: `if len(expression) > j+2 and expression[j+2] == "@":` and you should be ok (untested) – urban Oct 18 '20 at 11:01
  • 1
    Just end your loop 2 items before the end of the list: `for j in range (len(expression) - 2):`, as a matching `"**"` satisfying your conditions can't be one of the last two items. – Thierry Lathuille Oct 18 '20 at 11:06

3 Answers3

1

My comment in an answer (altho pretty simple tbf)

>>> expression = [2, "**", 2, "@", "**"]
>>> for j in range (len(expression)):
...     if expression[j] == "**":
...         if (
...             len(expression) > j +2 
...             and expression[j+2] == "@"
...         ):
...             expression[j], expression[j+2] = expression[j+2], expression[j]
... 
>>> print(expression)
[2, '@', 2, '**', '**']

Explanation: if the current value is ** you are attempting to access j+2. However, your list might not have that index (for example what if ** is the last element?). To cater for this case I extend your if statement to first check for length and then check for j+2 values. If/when the first check/condition fails, the second condition is skipped (not checked) and thus the IndexError does not happen.

(updated the input list to show that even ** at the end of the list wont raise an error)

urban
  • 5,392
  • 3
  • 19
  • 45
  • 1
    @Thierry Lathuille's comment is also a nice way to avoid that issue - check it out in the questions comments – urban Oct 18 '20 at 11:13
0

Try the below

# assuming "**" and "@" apper once in the list
a = [2, "**", 2, "@"]
idx_1 = a.index("**")
idx_2 = a.index("@")
# I need to change if and only if ** and @ are separated by another element
if abs(idx_1 - idx_2) > 1:
  del a[idx_1]
  a.insert(idx_1,"@")
  del a[idx_2]  
  a.insert(idx_2,"**")
print(a)
balderman
  • 22,927
  • 7
  • 34
  • 52
  • Deleting and reinserting is inefficient and uncessary. Just do `a[idx_2] = "**"` and so on... Also, it doesn't satisfy the condition that items have to be separated by exactly one element. – Thierry Lathuille Oct 18 '20 at 11:02
  • 1
    @ThierryLathuille did you try the code without calling `del`? – balderman Oct 18 '20 at 11:05
  • I like your answer @balderman as well as your comment there :) – Ice Bear Oct 18 '20 at 11:12
  • Again, deleting a value and reinserting another one at the same place is a totally inefficient way of modifying a value. So dont't do `del a[i]; a.insert(i, new_value)`, just do `a[i] = new_value`... – Thierry Lathuille Oct 18 '20 at 11:26
0

I thought about just getting the positions out of the list and then swapping them.

for idx , j in enumerate(a):
    if (j == "**"):
        pos1 = idx
    elif (j=="@"):
        pos2 = idx
    
a[pos1],a[pos2] = a[pos2],a[pos1]