Reverse accessing a list in python - python

a=[0,1,2,3]
for a[-1] in a :
print a[-1]
This Program gives me an output
0
1
2
2
I am not able to decode it. I was expecting it to print the list in reverse direction. or at least print 3 alone. Can someone help me to understand this output

at each iteration a[-1] (the last element of a) is assigned a value of a (the list becomes [0,1,2,0] then [0,1,2,1] then [0,1,2,2])
a=[0,1,2,3]
for a[-1] in a :
print(a[-1])
It "works" until the end (although forward, not reversed), where the last iteration yields the last iterated upon value: 2
Now of course if you just want to iterate a list backwards:
for i in reversed(a):
print(i)
reversed(a) is better than a[::-1] here because it doesn't create a list just to iterate on it.

You can use
for x in a[::-1]:
print x
Which goes "reads" the list in reverse order, one element at a time. A list is implicitly "read" like a[::1]. If you'd like to "read" a list in reverse order but every second element you could use a[::-2] which would give you [3, 1].
Or you can use
for x in reversed(a):
print x

Related

How these python for loop and slicing working?

How this statement will work?
j for i in range(5)
Example:
x1=(j for i in range(5))
will yield five 4's when iterated. Why it will not yield 01234 like when we replace j with i.
How this statement working?
a = [0, 1, 2, 3]
for a[-1] in a:
print(a[-1])
0
1
2
2
Your first example is a generator expression (the parentheses are required, so the first version you show doesn't actually make sense). The generator expression repeatedly yields j, which is not defined in the code you show. But from your description, it already has the value 4 in the environment you were testing in, so you see that value repeatedly. You never use the i value, which is what's getting the values from the range.
As for your other loop, for a[-1] in a keeps rebinding the last value in the list, and then printing it.
A for loop does an assignment to the target you give it between for and in. Usually you use a local variable name (like i or j), but you can use a more complicated assignment target, like self.index or, as in this case, a[-1]. It's very strange to be rewriting a value of your list as you iterate over it, but it's not forbidden.
You never get 3 printed out, because each of the previous assignments as you iterated overwrote it in the list. The last iteration doesn't change the value that gets printed, since you're assigning a[-1] to itself.
Your first question raises error when iterating, as j is not defined. If you are getting any response from that, you probably have defined a value for j above your generator, which sounds to be 4 in your code.
About your second question:
when you have something like this:
for i in a:
...
Actually you are are doing this in each cycle: i=a[0], i=a[1], ....
When you write a[-1] in a, it is kind of equal to: a[-1]=a[0], a[-1]=a[1], ... and you are changing last element of a each time. So at the end, the last element of your list would be 2.

Can I not put a for loop inside a for loop like this? [duplicate]

I'm currently developing a program in python and I just noticed that something was wrong with the foreach loop in the language, or maybe the list structure. I'll just give a generic example of my problem to simplify, since I get the same erroneous behavior on both my program and my generic example:
x = [1,2,2,2,2]
for i in x:
x.remove(i)
print x
Well, the problem here is simple, I though that this code was supposed to remove all elements from a list. Well, the problem is that after it's execution, I always get 2 remaining elements in the list.
What am I doing wrong? Thanks for all the help in advance.
Edit: I don't want to empty a list, this is just an example...
This is a well-documented behaviour in Python, that you aren't supposed to modify the list being iterated through. Try this instead:
for i in x[:]:
x.remove(i)
The [:] returns a "slice" of x, which happens to contain all its elements, and is thus effectively a copy of x.
When you delete an element, and the for-loop incs to the next index, you then skip an element.
Do it backwards. Or please state your real problem.
I think, broadly speaking, that when you write:
for x in lst:
# loop body goes here
under the hood, python is doing something like this:
i = 0
while i < len(lst):
x = lst[i]
# loop body goes here
i += 1
If you insert lst.remove(x) for the loop body, perhaps then you'll be able to see why you get the result you do?
Essentially, python uses a moving pointer to traverse the list. The pointer starts by pointing at the first element. Then you remove the first element, thus making the second element the new first element. Then the pointer move to the new second – previously third – element. And so on. (it might be clearer if you use [1,2,3,4,5] instead of [1,2,2,2,2] as your sample list)
Why don't you just use:
x = []
It's probably because you're changing the same array that you're iterating over.
Try Chris-Jester Young's answer if you want to clear the array your way.
I know this is an old post with an accepted answer but for those that may still come along...
A few previous answers have indicated it's a bad idea to change an iterable during iteration. But as a way to highlight what is happening...
>>> x=[1,2,3,4,5]
>>> for i in x:
... print i, x.index(i)
... x.remove(i)
... print x
...
1 0
[2, 3, 4, 5]
3 1
[2, 4, 5]
5 2
[2, 4]
Hopefully the visual helps clarify.
I agree with John Fouhy regarding the break condition. Traversing a copy of the list works for the remove() method, as Chris Jester-Young suggested. But if one needs to pop() specific items, then iterating in reverse works, as Erik mentioned, in which case the operation can be done in place. For example:
def r_enumerate(iterable):
"""enumerator for reverse iteration of an iterable"""
enum = enumerate(reversed(iterable))
last = len(iterable)-1
return ((last - i, x) for i,x in enum)
x = [1,2,3,4,5]
y = []
for i,v in r_enumerate(x):
if v != 3:
y.append(x.pop(i))
print 'i=%d, v=%d, x=%s, y=%s' %(i,v,x,y)
or with xrange:
x = [1,2,3,4,5]
y = []
for i in xrange(len(x)-1,-1,-1):
if x[i] != 3:
y.append(x.pop(i))
print 'i=%d, x=%s, y=%s' %(i,x,y)
If you need to filter stuff out of a list it may be a better idea to use list comprehension:
newlist = [x for x in oldlist if x%2]
for instance would filter all even numbers out of an integer list
The list stored in the memory of a computer. This deals with the pointer to a memory artifact. When you remove an element, in a by-element loop, you are then moving the pointer to the next available element in the memory address
You are modifying the memory and iterating thru the same.
The pointer to the element moves through the list to the next spot available.
So in the case of the Size being 5...enter code here
[**0**,1,2,3,4]
remove 0 ---> [1,**2**,3,4] pointer moves to second index.
remove 2 ---> [1,3,**4**] pointer moves to 3rd index.
remove 4 ---> [1,3]
I was just explaining this to my students when they used pop(1). Another very interesting side-effect error.
x=[1,**2**,3,4,5]
for i in x:
x.pop(1)
print(x,i)
[1, **3**, 4, 5] 1 at index 0 it removed the index 1 (2)
[1, **4**, 5] 3 at index 1 it removed the index 1 (3)
[1, 5] 5 at index 2 it removed the index 1 (4)
heh.
They were like why isnt this working... I mean... it did... exactly what you told it to do. Not a mind reader. :)

How does "list[x]" get only a single number from "list"?

list=(1,2,3,4)
for x in range(len(list)):
print(list[x])
1
2
3
4
but print(list) repeats the whole list 4 times, i.e.
1,2,3,4
1,2,3,4
1,2,3,4
How does the x in the print(list[x]) cause only single numbers to come out? Like how could you put what the [x] is doing in English?
The 'x' represents a number, which changes with each iteration of the for loop. When you do list[x], you are getting the xth number in list, starting at 0.
x represents the index within the list. Using list[x] is actually picking the element in the x position from the list.
For each time through the loop x takes on values 0, 1, 2, etc. list[x] is then list[0], list[1], where the square brackets extract a single list element.
Note: for good python form consider using a different variable name (such as lst) to avoid confusion with builtin python names.
You are telling the program that in range of list's length (4), run the following command:
print(list)
So it will print all the list items 4 times.
"Like how could you put what the [x] is doing in English?"
for item in list:
print(item)
And as other friends said, don't use "list" for variable name.

Remove a list while iterating in python without copying to new list

I want to remove elements from my list while iterating the list. I don't think copying the list and performing the operations on either one will solve my problem.
I have a nested list,
Here as soon as I get the leftmost or the rightmost values of the list == maximum I append it to a new list Lst1 and pop the element from the original list else break from the loop.
lst= [[4, 3, 2, 1, 3, 5]]
lst1=[]
for i in range(len(lst)):
if lst[0][i]==max(lst[0]):
lst1.append(lst[0][i])
lst.remove(lst[0][i])
elif lst[0][maxsize_lst-1]==max(lst[0]):
lst1.append(lst[0][maxsize_lst-1])
lst.remove(lst[0][maxsize_lst-1])
else :
print("NO")
break;
I'm getting the below errors and sometimes I get index out of range probably because i'm removing the element and iterating the list again
ValueError: list.remove(x): x not in list
The output of list 1 should look something like:
5 4 3 3 2 1
EDIT
The final list is coming in the descending order but it's not a sorting problem. Here i will be first picking either the leftmost or rightmost element first and check if it is == max(lst). if any of them follows true i'm removing that element.Now my list would be having one less element. If it was leftmost pop then i would resume from index 1 to last ,vice versa if it was rightmost i would again do the same search from index 0 to maxsize-2 to do the same search. If nothing follows like leftmost or rightmost != Max(lst) then break and print No
There is a much simple solution:
lst = [[4, 3, 2, 1, 3, 5]]
print(sorted(lst[0], reverse=True))
Result:
[5, 4, 3, 3, 2, 1]
First of all, you want to remove values from your nested list at the level where they are: lst.remove(x) only searches in lst, not in lst and all possible lists nested in lst. That will solve your problem with ValueError.
Second, simply running your example by hand tells you why it isn't working: you are never updating maxsize_lst, therefore as soon as you pop out an item this value is no longer valid. A simple solution would be to use python's negative indexing system to access the last value of your list: lst[-1]. But even then, if your goal is to get all values in your list sorted, your code cannot do it: on the first step of your example already,
with i=0, you remove 5 from the list (last item, max of values)
next step, i=1, and you will never again access the value at i=0
But then maybe that's not a problem for you, it isn't clear what you want to achieve with your code...
Edit: I re-read your question, if what you want is actually to pop the left/rightmost value when it is a maximum from your old list to your new list, then you shouldn't be iterating over your list with a for loop but rather using a while loop like that:
size_lst = len(lst[0])
while size_lst > 0:
if lst[0][0] == max(lst[0]):
# Leftmost element max of the list
lst1.append(lst[0].pop(0) # Pop leftmost element of lst[0] into lst1
size_lst -= 1 # Record that you decreased the size of your list
elif lst[0][-1] == max(lst[0]):
# Same with the rightmost element
lst1.append(lst[0].pop(-1)
size_lst -= 1
else:
break
It looks like you're sorting the first list. This can be achieved much more easily. The sorted function will automatically sort it from least to greatest, and then you can use the reversed function to sort greatest to least. Try:
lst1 = reversed(sorted(lst[0]))
EDIT: If you need to use the method put forward in the original code, I caught a mistake in your for loop. You are taking the length of lst, not the sublist, the code should be the following:
for i in range(len(lst[0])):
Also, I don't know if you established a variable maxsize_list, but you can get the last element of the list easily with lst[0][-1]. Finally, your error is being caused by you trying to remove lst[0][-1] from lst, not lst[0]. This is your code without syntax errors. I believe there is a symantic error that occurs when the maximum is at the end.
lst= [[4,3,2,1,3,5]]
lst1=[]
for i in range(len(lst[0])):
if lst[0][i]==max(lst[0]):
lst1.append(lst[0][i])
lst[0].remove(lst[0][i])
elif lst[0][-1]==max(lst[0]):
lst1.append(lst[0][-1])
lst[0].remove(lst[0][-1])
else :
print("NO")
break;
#PRMoureu's comment is a big hint to the answer about what's going wrong.
In your sample data, your list is of size 6. You're iterating over the indices, so from 0 through 5. As you progress through your loop, you remove things from your list, but then you continue to look for it. So at some point, you look at lst[0][i] for an i that no longer exists, and that's why you get your error. This will happen as long as you're using the index.
But you don't need the index into the list. You need the value at it. So the recommendation is a very good idea: simply iterate on the list itself, instead of on its indices. This will give you code like this:
lst= [[4, 3, 2, 1, 3, 5]]
lst1=[]
for val in lst[0]:
print(val)
if val == max(lst[0]):
print("a")
lst1.append(val)
lst[0].remove(val)
print(lst[0])
# this shouldn't even be necessary; you should be able to sort just as well without it
elif lst[0][-1]==max(lst[0]):
print("b")
lst1.append(lst[0][-1])
lst[0].remove(lst[0][-1])
else :
print("NO")
break;
Note, python wouldn't use a construct like maxsize_lst. Instead, it would just use lst[0][-1] to get the last element. I fixed the several places where you were referring to lst instead of lst[0], and made your lst definition actually be valid by putting commas between the values.
When I run this code, I get "NO" as a result. Leave the print statements in to understand why. The first time, you have a 4. It isn't the max, so you look to see if the last value is a max. It is, so it gets added. The second time, you have a three, which is again not your max. Nor is the last remaining value (the other 3), so it says "NO" and aborts. You've got the idea by using a break statement, but you need another loop around it that would continue until your list is empty.
To get this to work, you need an outer loop similar to as follows:
lst= [[4, 3, 2, 1, 3, 5]]
lst1=[]
reset = True
while len(lst[0]) != 0 and reset:
print(lst[0], lst1)
reset = False
for val in lst[0]:
print(val)
if val == max(lst[0]):
print("a")
lst1.append(val)
lst[0].remove(val)
reset = True
break
elif lst[0][-1]==max(lst[0]):
print("b")
lst1.append(lst[0][-1])
lst[0].remove(lst[0][-1])
reset = True
break
else :
print("NO")
break
Note that I did need to add a break even when popping from the left side. Without that, the end result was that lst1 had a value of [5, 4, 3, 3, 2], and lst[0] still had [1] in it.

Why does Python skip elements when I modify a list while iterating over it?

I'm currently developing a program in python and I just noticed that something was wrong with the foreach loop in the language, or maybe the list structure. I'll just give a generic example of my problem to simplify, since I get the same erroneous behavior on both my program and my generic example:
x = [1,2,2,2,2]
for i in x:
x.remove(i)
print x
Well, the problem here is simple, I though that this code was supposed to remove all elements from a list. Well, the problem is that after it's execution, I always get 2 remaining elements in the list.
What am I doing wrong? Thanks for all the help in advance.
Edit: I don't want to empty a list, this is just an example...
This is a well-documented behaviour in Python, that you aren't supposed to modify the list being iterated through. Try this instead:
for i in x[:]:
x.remove(i)
The [:] returns a "slice" of x, which happens to contain all its elements, and is thus effectively a copy of x.
When you delete an element, and the for-loop incs to the next index, you then skip an element.
Do it backwards. Or please state your real problem.
I think, broadly speaking, that when you write:
for x in lst:
# loop body goes here
under the hood, python is doing something like this:
i = 0
while i < len(lst):
x = lst[i]
# loop body goes here
i += 1
If you insert lst.remove(x) for the loop body, perhaps then you'll be able to see why you get the result you do?
Essentially, python uses a moving pointer to traverse the list. The pointer starts by pointing at the first element. Then you remove the first element, thus making the second element the new first element. Then the pointer move to the new second – previously third – element. And so on. (it might be clearer if you use [1,2,3,4,5] instead of [1,2,2,2,2] as your sample list)
Why don't you just use:
x = []
It's probably because you're changing the same array that you're iterating over.
Try Chris-Jester Young's answer if you want to clear the array your way.
I know this is an old post with an accepted answer but for those that may still come along...
A few previous answers have indicated it's a bad idea to change an iterable during iteration. But as a way to highlight what is happening...
>>> x=[1,2,3,4,5]
>>> for i in x:
... print i, x.index(i)
... x.remove(i)
... print x
...
1 0
[2, 3, 4, 5]
3 1
[2, 4, 5]
5 2
[2, 4]
Hopefully the visual helps clarify.
I agree with John Fouhy regarding the break condition. Traversing a copy of the list works for the remove() method, as Chris Jester-Young suggested. But if one needs to pop() specific items, then iterating in reverse works, as Erik mentioned, in which case the operation can be done in place. For example:
def r_enumerate(iterable):
"""enumerator for reverse iteration of an iterable"""
enum = enumerate(reversed(iterable))
last = len(iterable)-1
return ((last - i, x) for i,x in enum)
x = [1,2,3,4,5]
y = []
for i,v in r_enumerate(x):
if v != 3:
y.append(x.pop(i))
print 'i=%d, v=%d, x=%s, y=%s' %(i,v,x,y)
or with xrange:
x = [1,2,3,4,5]
y = []
for i in xrange(len(x)-1,-1,-1):
if x[i] != 3:
y.append(x.pop(i))
print 'i=%d, x=%s, y=%s' %(i,x,y)
If you need to filter stuff out of a list it may be a better idea to use list comprehension:
newlist = [x for x in oldlist if x%2]
for instance would filter all even numbers out of an integer list
The list stored in the memory of a computer. This deals with the pointer to a memory artifact. When you remove an element, in a by-element loop, you are then moving the pointer to the next available element in the memory address
You are modifying the memory and iterating thru the same.
The pointer to the element moves through the list to the next spot available.
So in the case of the Size being 5...enter code here
[**0**,1,2,3,4]
remove 0 ---> [1,**2**,3,4] pointer moves to second index.
remove 2 ---> [1,3,**4**] pointer moves to 3rd index.
remove 4 ---> [1,3]
I was just explaining this to my students when they used pop(1). Another very interesting side-effect error.
x=[1,**2**,3,4,5]
for i in x:
x.pop(1)
print(x,i)
[1, **3**, 4, 5] 1 at index 0 it removed the index 1 (2)
[1, **4**, 5] 3 at index 1 it removed the index 1 (3)
[1, 5] 5 at index 2 it removed the index 1 (4)
heh.
They were like why isnt this working... I mean... it did... exactly what you told it to do. Not a mind reader. :)

Categories

Resources