This question already has answers here:
Python: pop from empty list
(5 answers)
Closed 3 years ago.
N=8
f,g=4,7
indexList = range(N)
print indexList
print f, g
indexList.pop(f)
indexList.pop(g)
In this code I am getting an error stating that the pop index of g in indexList is out of range.
Here is the output:
[0, 1, 2, 3, 4, 5, 6, 7]
4 7
Traceback (most recent call last):
indexList.pop(g)
IndexError: pop index out of range
I don't understand, g has a value of 7, the list contains 7 values, why is it not able to return me the 7 in the list?
To get the final value of a list pop'ed, you can do it this way:
>>> l=range(8)
>>> l
[0, 1, 2, 3, 4, 5, 6, 7]
>>> l.pop(4) # item at index 4
4
>>> l
[0, 1, 2, 3, 5, 6, 7]
>>> l.pop(-1) # item at end - equivalent to pop()
7
>>> l
[0, 1, 2, 3, 5, 6]
>>> l.pop(-2) # one left of the end
5
>>> l
[0, 1, 2, 3, 6]
>>> l.pop() # always the end item
6
>>> l
[0, 1, 2, 3]
Keep in mind that pop removes the item, and the list changes length after the pop. Use negative numbers to index from the end of a list that may be changing in size, or just use pop() with no arguments for the end item.
Since a pop can produce these errors, you often see them in an exception block:
>>> l=[]
>>> try:
... i=l.pop(5)
... except IndexError:
... print "sorry -- can't pop that"
...
sorry -- can't pop that
After you pop the 4, the list only has 7 values. If you print indexList after your pop(f), it will look like:
[0, 1, 2, 3, 5, 6, 7]
Along with all the other answers, the important part about the pop() function is that it removes the value from the array, thus changing the indexes. After popping index 4, your list is left with 7 items. It is important to know that Python indexes starting at 0 so your 7 item list only contains indexes 0 through 6. That's why popping index 7 is out of bounds, it no longer exists.
Typically a "popping" function is used when implementing a stack or a queue where the goal is to get a value from a list of values waiting to be processed. To avoid processing the same data twice by accident, you make sure to remove it at the same time as retrieval.
Sometimes stacks and queues can be implemented with a peek operation that will return just the value without removing it but since Python implements stacks and queues just using regular arrays without any special wrapper, your peek function would be the standard array[index] call.
----EDIT----
It occurs to me that it could be the case that instead of removing the item at index 7, you wish to remove the value 7. If that's the case, you should call indexList.remove(7). This will remove the first instance of 7 in your list, no matter what its index (and throws an error if there is no value 7). I'm pretty sure you understand that pop() takes an index, though.
Just in case, take a look at the Python datastructures API for more information on what functions are available, what they do, and what arguments they take.
Related
Suppose that I have a list that has [0, 1, 2, 3 , 4, 5, 6] in it. I want to remove those elements that are greater than or equal to 3 and add those removed elements to the beginning of the list. So I wrote the code below:
list = [0, 1, 2, 3, 4, 5, 6]
new_list =[]
for number in list:
if number >= 3:
dropped_number = list.pop()
new_list.append(dropped_number)
new_list.sort()
new_list += list
print(new_list)
However, when I ran the code, the result was displayed as [5, 6, 0, 1, 2, 3 , 4]. Could anyone please explain to me at which step I did wrong here?
There are two issues with your code.
the number you obtain with list.pop() is not the one you just checked with your condition (it is merely the last one in the list)
When you reach 3, list.pop() removes 6,
When you reach 4, list.pop() removes 5,
You never reach 5 because you're at the end of what remains of the list at that point.
removing items from a list within a for-loop on the same list will cause the for-loop to skip items or complain that the list changed during iterations. So, even if you were to pop the appropriate number, your loop would miss items.
You also don't need to sort new_list every time you add to it, you can do it once at the end, but that just optimization.
Instead of a for-loop, you could use the sort method with a key parameter that returns a boolean indicating True for elements that do not meet your conditions (i.e that will be shifted to the right). Because Python's sort is stable, this will only place elements in two groups without otherwise changing their relative order.
L = [0, 2, 4, 6, 1, 3, 5]
L.sort(key=lambda x: not x>=3)
print(L) # [4, 6, 3, 5, 0, 2, 1]
If you need a more procedural solution, you can separate the values in two lists that you stick together at the end:
L = [0, 2, 4, 6, 1, 3, 5]
left,right = [], []
for x in L:
if x >= 3: left.append(x)
else: right.append(x)
L = left + right
# [4, 6, 3, 5, 0, 2, 1]
Modifying a list while iterating over it is usually problematic. What if instead you thought of the problem as building a new list out of two subsets of the original list?
>>> old_list = list(range(7))
>>> [i for i in old_list if i >= 3] + [i for i in old_list if i < 3]
[3, 4, 5, 6, 0, 1, 2]
The reason your program doesn't work is because you are modifying the list whilst searching through it. Instead, you can start by adding the elements >= 3 to a new list and then separately appending the elements < 3 to the list. Also, considering you are created a second 'new_list', there is no need to remove the elements from the first list.
Your new code:
list = [0, 1, 2, 3, 4, 5, 6]
new_list = []
# Append numbers greater than 3 to the new list
for number in list:
if number >= 3:
new_list.append(number)
# Append the numbers less than 3 to the new list
new_list += list[0:list.index(new_list[0])]
print(new_list)
Just to note, this method takes a section of the original list from position 0, to the position (.index) of the first item in the new list, which automatically generates the < 3 condition as the first item in the new list corresponds to the items before the >= 3 condition is met.
list[0:list.index(new_list[0])]
This question already has answers here:
Difference between del, remove, and pop on lists
(14 answers)
Closed 2 years ago.
data = [2, 4, 3, 1, 5, 10, 9]
data.pop()
print(data)
result: [2, 4, 3, 1, 5, 10]
the above is what i think makes sense
however,
data = [2, 4, 3, 1, 5, 10, 9]
Print(data.pop())
I got this
Result : 9
what is the difference?
In the 1st situation, you are printing a list of data. I have re-lined the code and added comments to make it more understandable:
Scenario 1:
data = [2, 4, 3, 1, 5, 10, 9] # the given data list
data.pop() # pop the last element off the list
print(data) # print 'data' (which is the list)
The reason why the result here is [2, 4, 3, 1, 5, 10] is because you are popping from the list, and then printing the list itself.
Scenario 2:
data = [2, 4, 3, 1, 5, 10, 9] # the given data list
print(data.pop()) # print the value returned by data.pop(), which is 9
# Result : 9
List.pop() is a method that returns the popped value from the list. So, by doing print(data.pop()), you are requesting to print the single popped value rather than the data list in its entirety.
See the Python documentation on the List.pop() method here.
The pop() method returns the value which was deleted from the list.
If you try to print that d.pop() it just returns the removed value.
When you use pop( ) it returns the last element of the list and internally it deletes last element from the list.
Note that in the first code you’re printing data ( the list ) and in the second one you’re printing what pop returns
I was practicing python 'list variable' with 'for loop', but was surprised to see that the order of the items in the list changed.
xlist=[1,2,3,4,5]
print(xlist)
#loop all items in the lxist
for item in xlist:
print(item)
#multiply each item by 5
xlist[xlist.index(item)] = item * 5
#print the list
print(xlist)
I was expecting the list order to be [5,10,15,20,25] but instead i got [25, 10, 15, 20, 5]
I am using python 3.8(32 version) using pycharm IDE.
Can anyone clarify why the order of the list has changed
You are not using the .index method correctly. Two problems, semantically, it doesn't mean what you think it means, it gives you the first index of some object in a list. So note, on your last iteration:
xlist.index(5) == 0
Because on your first iteration, you set:
xlist[0] = 1 * 5
The correct way to do this is to maintain and index as you iterate, either manually by using something like index = 0 outside the loop and incrementing it, or by iterating over a range and extracting the item using that index. But the pythonic way to do this is to use enumerate, which automatically provides a counter when you loop:
for index, item in enumerate(xlist):
xlist[index] = item*5
The other problem is even if your items were all unique and the index returned was correct, using .index in a loop is unnecessarily making your algorithm quadratic time, since .index takes linear time.
The index method returns the index of the first occurrence of the item you have passed as an argument (assuming it exists). So, by the time you reach the last element, i.e. 5 at index 4, the item at index 0 is also 5, so you get 5 * 5 at index 0 in the final result.
When the index method is searching for the 5th number (5) it locates the first index that has that value. At this point in time, index 0 (the 1st number) is also 5 so it multiplies index 0 by 5. A better way to loop through is to use the enumerate method to loop through each index and modify the number at that index, rather than find the index afterwards. This eliminates the troubles with the index method.
xlist=[1,2,3,4,5]
print(xlist)
#loop all items in the lxist
for i, item in enumerate(xlist):
print(item)
#multiply each item by 5
xlist[i] *= 5
#print the list
print(xlist)
Results:
[1, 2, 3, 4, 5]
1
[5, 2, 3, 4, 5]
2
[5, 10, 3, 4, 5]
3
[5, 10, 15, 4, 5]
4
[5, 10, 15, 20, 5]
5
[5, 10, 15, 20, 25]
This question already has answers here:
Reversing a list slice in python
(3 answers)
Closed 3 years ago.
Suppose we have this list:
>>> a = [x for x in range(10)]
>>> print(a)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Separately, both ways to slice work as expected:
>>> a[3:8]
[3, 4, 5, 6, 7]
>>> a[::-1]
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
But, when combined:
>>> a[3:8:-1]
[]
I would expect it to be [7, 6, 5 ,4, 3] or perhaps [6, 5, 4, 3, 2] (if reversing happened first).
It is also interesting to consider what happens when either start or stop parameters are not passed:
>>> a[:5:-1]
[9, 8, 7, 6]
This is almost what I would expect, only its one item short. Tested this with numpy and it seems to behave in the same way.
Whats going on here?
With
a[3:8:-1]
The start and stop positions of the slice aren't adjusted based on the step. With a negative step, you're having it go backwards from 3, but there are no elements with indices in the range 3 to 8 counting back from 3, so you get an empty list.
You need to set the start and stop accordingly:
a[8:3:-1]
Which will count back from 8 to 4.
a[3:8:-1] instructs python to start from 3 and go to 8 by steps of -1
This creates an empty list: it's not possible to reach 8 from 3 by adding -1 (just like list(range(3,8,-1)) which gives an empty list too)
When you do a[:5:-1] then start is the default start, which python sets to "end of list" so it "works"
Same as when you do a[::-1] the start & stop are the default ones, and python understands that they're from end to start (else this notation wouldn't be useable)
This behavior is explained in the documentation.
The slice of s from i to j is defined as the sequence of items with index k such that i <= k < j. If i or j is greater than len(s), use len(s). If i is omitted or None, use 0. If j is omitted or None, use len(s). If i is greater than or equal to j, the slice is empty.
The slice of s from i to j with step k.... stopping when j is reached (but never including j). When k is positive, i and j are reduced to len(s) if they are greater. When k is negative, i and j are reduced to len(s) - 1 if they are greater. If i or j are omitted or None, they become “end” values (which end depends on the sign of k).
Specifications:
I want to use the remove function (in lists) and I'd prefer to avoid typecasting.
l = [2, 3, 3, 4, 6, 4, 6, 5]
q=len(l)
for i in range (0, q):
for g in range (i+1, q):
if l[g]==l[i]:
q-=1 #decremented q to account for reduction in list size.
l.remove(l[g])
print(l)
Error: if l[g]==l[i]:
IndexError: list index out of range
I know that similar questions have been asked by users previously. As the aforementioned constraints were absent in them, I would like to request you to treat this as a separate question. Thanks!
>>> l = [2, 3, 3, 4, 6, 4, 6, 5]
>>> s = set(l)
>>> t = sorted(s)
>>> print(t)
[2, 3, 4, 5, 6]
Using set is a simple and straight-forward way to filter your collection. If you don't need the list in a specific order, you can just use the set from thereon. The sorted function returns a list (using the default ordering).
Since you mentioned you don't want typecasting, so my solution is using while loop
l = [2, 3, 3, 4, 6, 4, 6, 5]
q=len(l)
i = 0
while i<len(l):
g = i+1
while (g < q):
if l[g]==l[i]:
q-=1 #decremented q to account for reduction in list size.
l.remove(l[g])
g += 1
i += 1
print(l)
Now, allow me to explain what was the problem in your code. When you use range function, it holds the starting and the ending value at the first run of the loop, so even if you change the limits afterwards in the loop, still, it won't change the range loop so eventually, you get index out of bounds error.
Hope this helps you :)
Your solution does not work, because range() store the value of q, and will ignore the change of q's value later. Eg:
>>> m = 10
>>> for i in range(m):
... m=0
... print(i)
...
0
1
2
3
4
5
6
7
8
9
Even if I change m, range() will still go 10 times in the loop. So, when you change the size of the list, even if you change q, you will still try to reach elements that does not exist anymore.