I am trying to remove duplicates from a sorted list.
nums = [0,0,1,1,1,2,2,3,3,4]
for i in range(len(nums)):
a = nums.count(i)
if(a>1):
nums.pop(i)
I am getting [0, 1, 2, 3, 3, 4] but am expecting [0, 1, 2, 3, 4].
I see the logic I used worked halfway and removed duplicates until the value 2 but for some reason I don't understand it didn't work for the value 3.
Your logic won't exactly work here. Since you're looping through the number of items in the list and the length of the list is changing, you are at risk at running into an error (IndexError).
Here's another way to approach this problem.
nums = [0,0,1,1,1,2,2,3,3,4]
new_list = []
for i in range(len(nums)):
num = nums[i]
if num not in new_list:
new_list.append(num)
print(new_list)
nums = [0,0,1,1,1,2,2,3,3,4]
seen_n = set()
for i, n in reversed(list(enumerate(nums))):
if n in seen_n:
del nums[i]
else:
seen_n.add(n)
print(nums)
Prints:
[0, 1, 2, 3, 4]
If you are iterating the elements by index number, then you need to remove the elements in reverse order so that the indices of the next elements you visit are not affected by the deletions of previous element. In this code we simply keep track of every unique value we see in a set and test each element of the list against membership in that set to see if it should be deleted.
I just wanted to add that there are many ways to solve this problem. But the question posed was, "Remove duplicates from a list." I take this literally and do not consider creating a second list with duplicates removed to be the same thing as removing duplicates from the original list. You have to ask yourself what if there are other references to the original list? Will they see the change? No.
Your problem is in your misunderstanding of nums.pop(i) : it won't delete all the i items, it would delete just that single item having index i.
So that nums.pop(3) deletes the second 2 item.
# [0,0,1,1,1,2,2,3,3,4]
# [ 0,1,1,1,2,2,3,3,4]
# [ 0, 1,1,2,2,3,3,4]
# [ 0, 1, 2,2,3,3,4]
# [ 0, 1, 2, 3,3,4]
This is a different approach, but it may be worth mentioning that you can also remove duplicates by converting to a dict and back.
nums = [0,0,1,1,1,2,2,3,3,4]
nums = list(dict.fromkeys(nums))
There are already lots of answers to this question, but it seems to me none of them are doing the obvious optimization that comes from the fact that the list is sorted, which means that if a number is a duplicate, it is necessarily identical to its predecessor.
This is how I would solve the question, then, using prev_n != n as the most efficient way to know n has not been seen yet:
nums = [0,0,1,1,1,2,2,3,3,4]
uniq_nums = [nums[0]]
prev_n = nums[0]
for n in nums:
if prev_n != n:
uniq_nums.append(n)
prev_n = n
print(uniq_nums)
try using two lists
nums = [0, 1, 2, 2, 3, 4]
nums2 = []
for i in nums:
if i not in nums2: nums2.append(i)
print(nums2)
edit: previous solution was ineffective, whoops
Related
I want to make a code that receives a random list and stores only positive numbers.
However, if I run it with the code I wrote, I only get positive numbers, but the order is reversed. What should I do?
As an example of the code, [3, 2, 1, 0] is displayed.
I want to print this out [0, 1, 2, 3].
def filter(list):
flist = []
for i in list:
if list[i]>=0:
flist.append(list[i])
else:
continue
return flist
list = [-1,-2,-3,-4,0,1,2,3]
print(filter(list))
for i in list iterates over the items in the list, not the indices. Since the first four items in the list are negative numbers, when you use them as indices, you end up iterating through the last half of the list in reverse order before reaching zero and then iterating through the first half of the list in forward order. (If all the items in the list didn't happen to be valid indices for that same list, you'd just get an IndexError instead.)
To iterate over all the items in the list in order by index use range and len:
# 'filter' and 'list' are both builtin names, don't redefine them
def filter_nat(nums):
flist = []
for i in range(len(nums)):
if nums[i]>=0:
flist.append(nums[i])
else:
continue
return flist
nums = [-1,-2,-3,-4,0,1,2,3]
print(filter_nat(nums)) # [0, 1, 2, 3]
It's simpler to iterate by value, though; you just need to use the value itself rather than trying to use it as an index:
def filter_nat(nums):
flist = []
for i in nums:
if i >=0:
flist.append(i)
return flist
nums = [-1,-2,-3,-4,0,1,2,3]
print(filter_nat(nums)) # [0, 1, 2, 3]
and it's simpler yet to use a comprehension instead of individually appending each item:
def filter_nat(nums):
return [i for i in nums if i >= 0]
nums = [-1,-2,-3,-4,0,1,2,3]
print(filter_nat(nums)) # [0, 1, 2, 3]
sort before returning the list
def filter(list):
flist = []
for i in list:
if list[i]>=0:
flist.append(list[i])
else:
continue
flist.sort()
return flist
list = [-1,-2,-3,-4,0,1,2,3]
print(filter(list))
Output:
[0, 1, 2, 3]
What do you want? Something in the same order as encountered reading from left to right or in increasing order? If it is in the order of reading in initial list, here is the answer:
function append() adds the element at the end of the list. Hence, you could either go over the list from the end to its beginning using append() or going in same order as you, use another fashion in order to add the element at the beginning. When sticking to your code, we would have
def filter(list):
flist = []
for i in list:
if list[i]>=0:
flist = [i] + flist
else:
continue
return flist
However, this could be written way more "pythonic" in the following way:
def filter(list):
return [i for i in list if i>=0]
Try for i in range (len(list)):
You would be looping through the number of items in the list. Personally it is easier and cleaner code for me but everyone has his preferences.
Try:
print(filter(list[::-1]))
I have tried execute this, but it dosn't work properly. My goal was to remove all numbers divided by 2. Could someone advise what is wrong. I really do not understand why '4', '8' are still there.
list = [2,4,9,0,4,6,8,3,43,44]
for e in list:
if e%2==0:
list.remove(e)
print(list)
You can use a list comprehension to generate a new list with only elements you want to keep.
newList = [x for x in oldList if not isEven(x)]
where the function isEven does something like:
def isEven(target):
return target % 2 == 0
By the way, your question is a duplicate of the following How to remove items from a list while iterating?
If you want to keep the list instead of creating a new one (the answer by Thomas Milox is a good one otherwise), you should iterate backward through the list by index. When you remove an element from a list while iterating forwards through the list you may jump over some elements, not processing them. Going backward ensures that no list element removals move any elements that you may still want to process.
Here is an example of how this may look for your code:
list = [2, 4, 9, 0, 4, 6, 8, 3, 43, 44]
for i in range(len(list) - 1, -1, -1): # start at the last element, go until the first one (index 0 - the last value in the range method will not be reached), go backwards
if list[i] % 2 == 0:
del list[i]
You can read a bit more about removing an element by index instead of by value here.
This is required since you would otherwise mutate the list on the wrong position for duplicate values. It may also be a bit faster, since removeneeds to iterate through the list, searching for the element to remove while del list[i] may look up the element that needs to be removed by index.
Iterating backward through a list is also covered here.
You can try to use list.pop() with the position of the element you want to remove.
The '2' and '4' are still there because they are skipped when you remove the number before them (When you remove the '2', the '4' is moved to the previous position)
Try this:
l = [2, 3, 4, 5, 9, 10,30,45]
new=[el for el in l if el % 2]
print(new)
Actually, when you remove an element from the list, the indexing gets changed. So, you can do this list comprehension.
Also you can use:
l = [2, 3, 4, 5, 9, 10,30,45]
new=[filter(lambda x: x % 2, l)]
print(new)
So, I don't understand why my output returns [1, 1, 3, 1, 3], when what I want, and thought, was [1,1,1].
the_list = [1, 2, 1, 2, 3, 1, 2, 3, 4]
target = 1
def keep(the_list, target):
index = 0
for x in the_list:
if x != target:
del the_list[index]
else:
pass
index += 1
print(the_list)
When you delete an item from a list at a specific index, all the items after the specified index get moved forward by 1 since there can be no gap in a list, so after you delete the 2 at index 1, for example, in the next iteration x would become 2 at the index 2 when it used to be at index 3, so your own index variable would be pointing to the wrong item.
To delete items from a list in-place, you should instead count backwards from the end of the list so that the re-indexing of the list after you delete an item won't affect the accuracy of your index counter:
def keep(the_list, target):
index = len(the_list) - 1
while index >= 0:
if the_list[index] != target:
del the_list[index]
index -= 1
so that:
the_list = [1, 2, 1, 2, 3, 1, 2, 3, 4]
target = 1
keep(the_list, target)
print(the_list)
would output:
[1, 1, 1]
But keep in mind that deleting an item from a list is inherently inefficient since it has an average time complexity of O(n) for having to move items after the given index, so deleting multiple items from a list becomes quadratic in complexity. It's much more efficient to use list comprehension to construct a new list from the old one by retaining only items that are equal to the target value. So even though the code above shows you how to correctly delete items from a list in-place, you should not actually use it in any production code.
It is gonna be much cleaner if you use list comprehension to check your list elements and return only what you need.
the_list = [1, 2, 1, 2, 3, 1, 2, 3, 4]
target = 1
new_list = [i for i in the_list if i == target]
List comprehensions are much more readable because its intent is explicit. A loop maybe used to do a lot of different things like more complex aggregations or more processing tasks. In contrast, a list comprehension is only meant to do one thing which is build a new list.
I'm a beginner at python coding and I've been trying to do this exercise to remove all adjacent characters from a list of integers to a single element using the following block of code:
#nums is a list of integers with repeating adjacent characters. For eg. [1,2,2,2,3,3]
length = len(nums)
for i in range(length):
while nums[i] == nums[i+1]:
del(nums[i+1])
length-=1
I keep getting the list index out of range error on the while loop, but since neither of the loops depends on the indices of nums directly, then how am I violating the bounds of the list?
I have previously checked with other answers to the same problem which all have solutions that relate to list comprehension which I don't want to apply to my code. Also, I'm aware of other approaches to removing adjacent elements but I would like to know what was wrong with my bit of code here for learning purposes. Thanks in advance.
I've added a check for confirming length. Is this what you want? It simply executes the loop if items are available.
nums = [1, 2, 2, 2, 3, 3]
length = len(nums)
for i in range(length):
while i < length-1 and nums[i] == nums[i + 1] :
del (nums[i + 1])
length -= 1
print(nums)
There are 2 errors here.
You are iterating over a range which cannot be indexed; in this case, the nums[len(nums)] does not exist. Remember Python indexing begins at 0.
You are modifying your list as you iterate. Don't do this, as the length of your list will change and your iteration will not work as intended.
Here is some code that works:
nums = [1,2,2,2,3,3]
length = len(nums)
items = []
for i in range(length-1):
if nums[i] == nums[i+1]:
items.append(i+1)
res = [i for i, j in enumerate(nums) if i not in items]
# [1, 2, 3]
(Python 3.6)
If you want remove all duplicate and keep the ordering:
>>> a = [1, 1, 2, 2, 2, 3, 4, 6, 3 ]
>>> list(dict.fromkeys(a))
[1, 2, 3, 4, 6]
But it will remove the last 3 too.
You can track array from end to start by length-1 like this:
nums = [1,2,2,2,3,3]
length = len(nums)
for i in reversed(range(length-1)):
while (nums[i] == nums[i-1]):
del(nums[i])
print(nums)
write a python program to delete val from array:
the first program is:
class Solution(object):
def removeElement(self,nums,val):
for x in nums:
if x == val:
nums.remove(val)
return len(nums)
when the nums is [3,3], the val is 3, the output is :1
the second program is :
class Solution(object):
def removeElement(self,nums,val):
while val in nums:
nums.remove(val)
return len(nums)
the nums is [3,3],the val is 3,the output is :0
could you please tell me the difference and reason
Python has a hard time iterating through something that is being changed during the iteration. It might fail or return unexpected results or never exit the iteration.
Because a container doesn't even keep track of iterators that are out
on it, much less hook even altering-method to loop over every such
iterator and somehow magically let each iterator know about the
alterations. It would be a lot subtle, complex code, and checks
slowing down very frequent operations.
nums=[3,4]
val=3
for x in nums:
print(x,nums)
if x > val:
nums.append(4)
The above code will not exit the iteration.So the correct way is to use a list comprehension to create a new list containing only the elements you don't want to remove:
print([i for i in nums if i!=val])
Or in-place alteration:
nums[:] = [i for i in nums if i!=val]
Hope this helps.
In the first example you are deleting an element inside the list while iterating over the elements of the list. That's a problem because you will run out of iterations after the first remove.
If the list is, for example, l = [1, 1, 2, 2] and you call removeElement(l, 2) after the first 2 is deleted, the length of l will be 3 and there won't be any iterations left (the list will be [1, 1, 2] and you will be at iteration number 3, that's why the loop stops and you get returned [1, 1, 2]).
In the second case, the while statement is: while there is still a 2 in the list, continue removing 2 from the list. This way, after the first iteration the l will look like [1, 1, 2] and there would still be a 2 inside it, so the loop continues and you get [1, 1].
You can apply this example on your case with l = [3, 3] and see if you understand it.
Hope that helped :)