ValueError: list.remove(x): x not in list python - python

I'm trying to sort a list from smallest to biggest integers. Unfortunately I get the error stated above when I try to run it.
Traceback (most recent call last):
File "lesson_4/selection_sort.py", line 24, in <module>
print selection_sort([-8, 8, 4, -4, -2, 2]) # [-8, -4, -2, 2, 4, 8]
File "lesson_4/selection_sort.py", line 14, in selection_sort
lst.remove(min)
ValueError: list.remove(x): x not in list
Here is the code of selection_sort.py
def selection_sort(lst):
sorted = []
list_len = len(lst) # Store this now because our loop will make it
# smaller
min = lst[0]
i = 1
while list_len > 0:
while i < list_len:
item = lst[i]
if item < min:
min = item
i += 1
lst.remove(min)
sorted.append(min)
return sorted
# Test Code
print "Testing"
print selection_sort([-8, 8, 4, -4, -2, 2]) # [-8, -4, -2, 2, 4, 8]
Thank for helping me out!

On your first pass through the list, you find the minimum element. However, on your second pass, min is still set to the minimum element in the original list. As a result, item < min is never true, and min forever remains the minimum element of the original list. Then when you try to remove it, you can't, because you already got rid of that item on the previous pass (unless there is a tie for the minimum, in which case this will happen as soon as all those elements are removed).
To solve this, just move min = lst[0] inside the first loop, so you reset it to a valid value each time.
You've also got some other issues, which I will mention here briefly:
You never update list_len, so you'll get an error at the end of the second pass through the outer loop (when you will attempt to go beyond the length of the list). You'd also loop forever if it didn't break bist. Luckily this whole variable is unneeded: you can use len(lst) in the outer loop, and replace your inner while loop with this:
for item in lst: # But see below regarding variable names!
if item < min:
min = item
This eliminates the need to track i separately and avoids any issues with the length of the list.
Next: this looks like homework, so it's probably not critical at this moment, but it's definitely worth mentioning: if I pass a list to a function called selection_sort, I would be very surprised to discover that after being sorted, my original list is now empty!! It's generally bad form to modify an input unless you're doing so explicitly (e.g. an in-place sort), so I highly recommend that you do all your work on a copy of the input, to avoid deleting all the content of the original:
lst_copy = lst[:] # If `lst` contains mutable objects (e.g. other lists), use deepcopy instead!
# Do stuff with lst_copy and avoid modifying lst
Finally, you've got two variables shadowing built-in functions: sorted and min. While this will technically work, it's poor form, and it's best to get into the habit of not naming local variables the same as builtins. By convention, if it's really the best name for the object, you can just add an underscore to the name to distinguish it from the builtin: min_ and sorted_ (or maybe better, output), for example.

If you simply want to sort the list, you can use inbuilt sort() function:
>>> lst=[-8, 8, 4, -4, -2, 2]
>>> lst.sort()
>>> lst
[-8, -4, -2, 2, 4, 8]
If you want to sort by your method, there are two slight mistakes in your code: you need to decrement lst_len every time you remove an element and reinitialize min to lst[0]. Also outer while should be while lst_len > 1 because list of length 1 is trivially sorted. Demo is given below:
>>> def selection_sort(lst):
sorted = []
list_len = len(lst) # Store this now because our loop will make it
# smaller
min = lst[0]
i = 1
while list_len > 1:
while i < list_len:
item = lst[i]
if item < min:
min = item
i += 1
lst.remove(min)
list_len-=1 # decrement length of list
min=lst[0] # reinitialize min
sorted.append(min)
return sorted
>>> selection_sort([-8, 8, 4, -4, -2, 2])
[8, 4, -4, -2, 2]

Related

Updating the range of list in function argument

Problem to solve: Define a Python function remdup(l) that takes a non-empty list of integers l
and removes all duplicates in l, keeping only the last occurrence of each number. For instance:
if we pass this argument then remdup([3,1,3,5]) it should give us a result [1,3,5]
def remdup(l):
for last in reversed(l):
pos=l.index(last)
for search in reversed(l[pos]):
if search==last:
l.remove(search)
print(l)
remdup([3,5,7,5,3,7,10])
# intended output [5, 3, 7, 10]
On line 4 for loop I want the reverse function to check for each number excluding index[last] but if I use the way I did in the above code it takes the value at pos, not the index number. How can I solve this
You need to reverse the entire slice, not merely one element:
for search in reversed(l[:pos]):
Note that you will likely run into a problem for modifying a list while iterating. See here
It took me a few minutes to figure out the clunky logic. Instead, you need the rest of the list:
for search in reversed(l[pos+1:]):
Output:
[5, 3, 7, 10]
Your original algorithm could be improved. The nested loop leads to some unnecessary complexity.
Alternatively, you can do this:
def remdup(l):
seen = set()
for i in reversed(l):
if i in seen:
l.remove(i)
else:
seen.add(i)
print(l)
I use the 'seen' set to keep track of the numbers that have already appeared.
However, this would be more efficient:
def remdup(l):
seen = set()
for i in range(len(l)-1, -1, -1):
if l[i] in seen:
del l[i]
else:
seen.add(l[i])
print(l)
In the second algorithm, we are iterating over the list in reverse order using a range, and then we delete any item that already exists in 'seen'. I'm not sure what the implementation of reversed() and remove() is, so I can't say what the exact impact on time/space complexity is. However, it is clear to see exactly what is happening in the second algorithm, so I would say that it is a safer option.
This is a fairly inefficient way of accomplishing this:
def remdup(l):
i = 0
while i < len(l):
v = l[i]
scan = i + 1
while scan < len(l):
if l[scan] == v:
l.remove(v)
scan -= 1
i -= 1
scan += 1
i += 1
l = [3,5,7,5,3,7,10]
remdup(l)
print(l)
It essentially walks through the list (indexed by i). For each element, it scans forward in the list for a match, and for each match it finds, it removes the original element. Since removing an element shifts the indices, it adjusts both its indices accordingly before continuing.
It takes advantage of the built-in the list.remove: "Remove the first item from the list whose value is equal to x."
Here is another solution, iterating backward and popping the index of a previously encountered item:
def remdup(l):
visited= []
for i in range(len(l)-1, -1, -1):
if l[i] in visited:
l.pop(i)
else:
visited.append(l[i])
print(l)
remdup([3,5,7,5,3,7,10])
#[5, 3, 7, 10]
Using dictionary:
def remdup(ar):
d = {}
for i, v in enumerate(ar):
d[v] = i
return [pair[0] for pair in sorted(d.items(), key=lambda x: x[1])]
if __name__ == "__main__":
test_case = [3, 1, 3, 5]
output = remdup(test_case)
expected_output = [1, 3, 5]
assert output == expected_output, f"Error in {test_case}"
test_case = [3, 5, 7, 5, 3, 7, 10]
output = remdup(test_case)
expected_output = [5, 3, 7, 10]
assert output == expected_output, f"Error in {test_case}"
Explanation
Keep the last index of each occurrence of the numbers in a dictionary. So, we store like: dict[number] = last_occurrence
Sort the dictionary by values and use list comprehension to make a new list from the keys of the dictionary.
Along with other right answers, here's one more.
from iteration_utilities import unique_everseen,duplicates
import numpy as np
list1=[3,5,7,5,3,7,10]
dup=np.sort(list((duplicates(list1))))
list2=list1.copy()
for j,i in enumerate(list2):
try:
if dup[j]==i:
list1.remove(dup[j])
except:
break
print(list1)
How about this one-liner: (convert to a function is easy enough for an exercise)
# - one-liner Version
lst = [3,5,7,5,3,7,10]
>>>list(dict.fromkeys(reversed(lst)))[::-1]
# [5, 3, 7, 10]
if you don't want a new list, you can do this instead:
lst[:] = list(dict.fromkeys(reversed(lst)))[::-1]

Python: List indexing for loop deletion glitch

This is a very tricky problem to explain. I was playing around with Python's list methods, particularly the del index object. I wanted to create a simple script that would create a list of integers from 1 to 100, and then a for loop which would delete the odd numbers from the list.
Here is the script I wrote:
def main():
num = list(range(1,101))
print(num)
for i in range(0,101):
del num[i]
print(num)
main()
Seems like it would work right? I thought so too, until I ran it.
I am not sure why, but when i was passed to the del num[i] index, the number itself doubled.
When I ran it, I received IndexError: list assignment index out of range.
When I changed the parameters from range(0,101) to range(0,10), I discovered that it deleted all the odd numbers from 1 to 20.
In other words, i in the index is doubling when it shouldn't. Can I get some information about this?
The size of the list is reduced when you delete items. After about 50 loop iterations, you have about 50 items in the list, so the nest iteration tries to delete something outside the list.
Here's a simulated run:
>>> a = [1, 2, 3, 4, 5]
>>> a
[1, 2, 3, 4, 5]
>>> del a[0]
>>> a
[2, 3, 4, 5]
>>> del a[1]
>>> a
[2, 4, 5]
>>> del a[2]
>>> a
[2, 4]
>>> del a[3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list assignment index out of range
When you use the del keyword inside of your for loop, it's completely removing the item from the original num list you made. By doing so, the length of your list is getting smaller and smaller with each iteration through the loop.
This also explains why it's only removing the odd numbers, since the indexes are shifting down after each delete, allowing the even numbers to slip through the cracks of your program:
num = range(1, 5) # num = [1, 2, 3, 4]
del num[0] # results in num = [2, 3, 4]
del num [1] # deletes 3, and num now = [2, 4]
etc. for a longer loop
To delete the odd numbers, consider using a conditional to check the %2 == 0 status and the list.remove() method:
num = range(1, 101)
for i in num:
if i % 2 != 0:
num.remove(i)
print(num)
Or a list comprehension inside your main() function:
return [x for x in num if x % 2 == 0]
Your iterate through the range from 0 to 101, and delete one item from num on each iteration, so the length of the num is reducing and you finally get an IndexError.
This problem is verymuch similar to mutating a list while iterating over the same list, go through below link which helps you to understand this situation better.
http://gsb-eng.com/why-python-list-mutation-is-not-a-good-idea/
for the second for loop, you should use half of the total indices, because at the 50th iteration, 50 elements would be removed, so the total index of the list is reduced to 50 , hence at the 51st iteration , it displays an out of range error.

Writing Python code that works like the reverse() function

I'm looking to break down the reverse() function and write it out in code for practice. I eventually figured out how to do it (step thru the original list backwards and append to the new 'reversed' list) but wondering why this doesn't work.
def reverse(list):
newlist = []
index = 0
while index < len(list):
newlist[index] = list[(len(list)) - 1 - index]
index = index + 1
return newlist
list = [1, 2, 3, 4, 5]
print(reverse(list))
In Python, you cannot access/update an element of a list, if the index is not in the range of 0 and length of the list - 1.
In your case, you are trying to assign to element at 0, but the list is empty. So, it doesn't have index 0. That is why it fails with the error,
IndexError: list assignment index out of range
Instead, you can use append function, like this
newlist.append(list[(len(list)) - 1 - index])
Apart from that, you can use range function to count backwards like this
for index in range(len(list) - 1, -1, -1):
newlist.append(list[index])
you don't even have to increment the index yourself, for loop takes care of it.
As suggested by #abarnert, you can actually iterate the list and add the elements at the beginning every time, like this
>>> def reverse(mylist):
... result = []
... for item in mylist:
... result.insert(0, item)
... return result
...
>>> reverse([1, 2, 3, 4, 5])
[5, 4, 3, 2, 1]
If you want to create a new reversed list, you may not have to write a function on your own, instead you can use the slicing notation to create a new reversed list, like this
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[::-1]
[5, 4, 3, 2, 1]
but this doesn't change the original object.
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[::-1]
[5, 4, 3, 2, 1]
>>> mylist
[1, 2, 3, 4, 5]
if you want to change the original object, just assign the slice back to the slice of the original object, like this
>>> mylist
[1, 2, 3, 4, 5]
>>> mylist[:] = mylist[::-1]
>>> mylist
[5, 4, 3, 2, 1]
Note: reversed actually returns a reverse iterator object, not a list. So, it doesn't build the entire list reversed. Instead it returns elements one by one when iterated with next protocol.
>>> reversed([1, 2, 3, 4, 5])
<list_reverseiterator object at 0x7fdc118ba978>
>>> for item in reversed([1, 2, 3, 4, 5]):
... print(item)
...
...
5
4
3
2
1
So, you might want to make it a generator function, like this
>>> def reverse(mylist):
... for index in range(len(mylist) - 1, -1, -1):
... yield mylist[index]
...
...
>>> reverse([1, 2, 3, 4, 5])
<generator object reverse at 0x7fdc118f99d8>
So the reverse function returns a generator object. If you want a list, then you can create one with list function, like this
>>> list(reverse([1, 2, 3, 4, 5]))
[5, 4, 3, 2, 1]
if you are just going to process it one by one, then iterate it with a for loop, like this
>>> for i in reverse([1, 2, 3, 4, 5]):
... print(i)
...
...
5
4
3
2
1
First off don't override build-ins (list in your case) second newlist has a len of 0 therefore cannot be accessed by index.
def reverse(mylist):
newlist = [0] * len(mylist)
index = 0
while index < len(mylist):
newlist[index] = mylist[(len(mylist)) - 1 - index]
index = index + 1
return newlist
mylist = [1, 2, 3, 4, 5]
print(reverse(mylist))
you can create a list with values of the same lenght as your input list like so
newlist = [0] * len(mylist)
You need to use list.append. newlist[0] is a valid operation, if the list has atleast one element in it, but newlist is empty in this very first iteration. Also, list is not a good name for a variable, as there is a python builtin container with the same name:
def reverse(lst):
newlist = []
index = 0
while index < len(lst):
newlist.append(lst[(len(list)) - 1 - index])
index += 1
return newlist
list = [1, 2, 3, 4, 5]
print(reverse(list))
You can't assign to an arbitrary index for a 0-length list. Doing so raises an IndexError. Since you're assigning the elements in order, you can just do an append instead of an assignment to an index:
newlist.append(l[(len(l)) - 1 - index])
Append modifies the list and increases its length automatically.
Another way to get your original code to work would be to change the initialization of newlist so that it has sufficient length to support your index operations:
newlist = [None for _ in range(len(l))]
I would also like to note that it's not a good idea to name things after built-in types and functions. Doing so shadows the functionality of the built-ins.
To write the function you're trying to write, see thefourtheye's answer.
But that isn't how reverse works, or what it does. Instead of creating a new list, it modifies the existing list in-place.
If you think about it, that's pretty easy: just go through half the indices, for each index N, swap the Nth from the left and the Nth from the right.*
So, sticking with your existing framework:
def reverse(lst):
index = 0
while index < len(lst)/2:
lst[index], lst[len(lst) - 1 - index] = lst[len(lst) - 1 - index], lst[index]
index = index + 1
As a side note, using while loops like this is almost always a bad idea. If you want to loop over a range of numbers, just use for index in range(len(lst)):. Besides reducing three lines of code to one and making it more obvious what you're doing, it removes multiple places where you could make a simple but painful-to-debug mistake.
Also, note that in most cases, in Python, it's easier to use a negative index to mean "from the right edge" than to do the math yourself, and again it will usually remove a possible place you could easily make a painful mistake. But in this particular case, it might not actually be any less error-prone…
* You do have to make sure you think through the edge cases. It doesn't matter whether for odd lists you swap the middle element with itself or not, but just make sure you don't round the wrong way and go one element too far or too short. Which is a great opportunity to learn about how to write good unit tests…
probably check this out:
def reverse(lst):
newList = []
countList = len(lst) - 1
for x in range(countList,-1,-1):
newList.append(lst[x])
return newList
def main():
lst = [9,8,7,6,5,4,2]
print(reverse(lst))
main()

Find and remove least common element in array (Python)

I am trying to find the least common element in an array of integers and remove it, and return the list in the same order.
This is what I have done, but when the list is [1, 2, 3, 4, 5], my function should return [], but returns [2, 4] instead.
def check(data):
for i in data:
if data.count(i) <= 1:
data.remove(i)
return data
data = [1, 2, 3, 4, 5]
print check(data)
Deleting items from a list you are iterating over causes you to skip items (ie the next item following each one you delete).
Instead, make a new list containing only the values you want to keep.
from collections import Counter
def check(data):
ctr = Counter(data)
least = min(ctr.values())
return [d for d in data if ctr[d] > least]
You shouldn't modify (especially delete) elements from a list while you are iterating over it.
What happened is:
Initially the iterator is at the 1st element, i.e. i = 1
Since d.count(1) is 1, so you delete 1 from the list.
The list is now [2,3,4,5], but the iterator advances to the 2nd element which is now the 3.
Since d.count(3) is 1 you delete it making the list [2,4,5]
The iterator advances to the 3rd element which is now 5.
Again you delete the 5 making the list [2,4].
Your algorithm should:
Get a count of all elements
Find the smallest count.
Find the elements with the smallest count.
Remove the elements found in step 3 from the list.
You shouldn't check data.count(i) <= 1. What happens in this case: [1, 1, 2, 2, 3, 3, 3]? 1 and 2 are the least common elements but you will never delete them. Likewise it is a bad idea to mutate a list in a for loop.
One thing you can do is use the Counter class.
Take an appropriate slice of the tail of the most_common() method (they entries get less frequent as you go down the list, so this is why you take the tail as opposed to the head).
Then you can repeatedly search the list for these occurrences and remove them until their are no occurrences left.
one another try:
def check(data):
ctr = Counter(data)
keys = ctr.keys()
vals = ctr.values()
least = []
m = min(vals)
for i in range(0,len(vals)):
if vals[i] == m:
least.append(keys[i])
print least
data = [1, 2, 3, 4, 5,1]
result = check(data)

Python: How to return a list without modifying the original list using a while loop only?

Say I have a function called everythird that takes a list as its parameter and returns a new list containing every third element of the original list, starting from index 0.
I know how to do this using slice notation (return everythird[0::3]), but we have to use a while loop only. If I type in everythird([1, 2, 3, 4, 5, 6, 7, 8]), I want it to return [1, 4, 7]. I tried a few different ways, but I'm not getting a list back, or I only get one value back. How do I return a list? Also how do you know for certain whether something modifies or doesn't modify an original list?
Thank you.
This is one of the ways I attempted this:
every_third([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
def everythird(l):
'''(list) -> list
Returns every third element of original list, starting at index 0'''
i = 0
while i < len(l):
print(l[i])
i += 3
This prints
1
4
7
If you need to do this with a while loop, you could do it by appending each element to a list rather than printing it, and then returning that list:
def everythird(l):
i = 0
ret = []
while i < len(l):
ret.append(l[i])
i += 3
return ret
Though as you note, it would certainly be preferably to do
def everythird(l):
return l[0::3]
Or if you were allowed to use a for loop:
def everythird(l):
ret = []
for i in range(0, len(l), 3):
ret.append(l[i])
return ret
Finally, if you were allowed to use a list comprehension:
def everythird(l):
return [l[i] for i in range(0, len(l), 3)]
The slice indexing is certainly the best, but in any case a while loop might be the worst way to do it.

Categories

Resources