I have a doctest written:
def extract_second(triples):
"""Given a list of triples, return a list with the second element of each triple
If an item is not a triple, return None for that element
>>> extract_second([('a',3,'x'),('b',4,'y')])
[3, 4]
>>> extract_second([('c',5,'z'),('d',6)])
[5, None]
>>> extract_second([('a',3,'x'),('b',4,'y')]) == [3, 4]
True
"""
for x in triples:
return x[1]
although the code is not returning the 1st index of the 2nd list inputted. Any ideas?
The reason you are only getting a single answer returned is because the return statement breaks your for loop as soon as it is encountered. it is the equivalent of something like this:
for x in list:
break
return x[1]
And a solution to your problem would be as such:
results = []
for i in triples:
if len(i) == 3:
results.append(i[1])
else:
results.append(None)
return results
This solution appends the answer to a list called results, and returns results after it is finished iterating through triples.
Related
This question already has answers here:
Remove all occurrences of a value from a list?
(26 answers)
Closed 1 year ago.
I want to remove the select element in sequence.
For example, I want to remove all 2 in sequence [1,2,3,2,3]
my code is
def remove_all(a_list, element):
for i in range(len(a_list)):
if element == a_list[i]:
a_list.remove(element)
return a_list
The output should be [1,3,3]
but the output of my code is [1,3,2,3]
I remove the first 2 but somehow the loop didn't go further to remove second 2. I wonder where is the problem of my code
Removing items in-place will almost certainly results in index errors.
[x for x in l if x != elem]
2 things.
You are modifying a list during iteration, I linked to a good read in my comment. also here
You return in the loop thus it stops at the return. Unindent the return, python is all about indentation unlike many other popular languages in this matter.
Try instead
Building another list for return:
def remove_all(a_list, element):
result = []
for ele in a_list:
if element != ele:
result.append(ele)
return result
Using a comprehension:
def remove_all(a_list, element):
return [ele for ele in a_list if ele != element]
a possible work-around is:
while element in a_list:
a_list.remove(element)
return element
But list comprehension works too, possibly even faster.
You should place the return after the loop finishes, something like:
def remove_all(a_list, element):
for i in a_list:
if element == i:
a_list.remove(element)
return a_list
a_list = [1, 2, 3, 2, 3]
result = remove_all(a_list, 2)
print(result)
Edit:
The code above does not work with the [2,2] case as pointed out in the comments.
Another solution would be:
myList = [2,2]
myList = list(filter((2).__ne__, myList))
print(myList)
Like that you don't copy the list anywhere, just is the list you are working on avoiding a list copy that can be expensive.
The __ne__ function checks that the element exists in the list returning True or False.
I am trying to remove adjacent duplicates from a list without using list mutations like del or remove. Below is the code I tried:
def remove_dups(L):
L = [x for x in range(0,len(L)) if L[x] != L[x-1]]
return L
print(remove_dups([1,2,2,3,3,3,4,5,1,1,1]))
This outputs:
[1, 3, 6, 7, 8]
Can anyone explain me how this output occurred? I want to understand the flow but I wasn't able to do it even with debugging in VS code.
Input:
[1,2,2,3,3,3,4,5,1,1,1]
Expected output:
[1,2,3,4,5,1]
I'll replace the variables to make this more readable
def remove_dups(L):
L = [x for x in range(0,len(L)) if L[x] != L[x-1]]
becomes:
def remove_dups(lst):
return [index for index in range(len(lst)) if lst[index] != lst[index-1]]
You can see, instead of looping over the items of the list it is instead looping over the indices of the array comparing the value at one index lst[index] to the value at the previous index lst[index-1] and only migrating/copying the value if they don't match
The two main issues are:
the first index it is compared to is -1 which is the last item of the list (compared to the first)
this is actually returning the indices of the non-duplicated items.
To make this work, I'd use the enumerate function which returns the item and it's index as follows:
def remove_dups(lst):
return [item for index, item in enumerate(lst[:-1]) if item != lst[index+1]] + [lst[-1]]
Here what I'm doing is looping through all of the items except for the last one [:-1] and checking if the item matches the next item, only adding it if it doesn't
Finally, because the last value isn't read we append it to the output + [lst[-1]].
This is a job for itertools.groupby:
from itertools import groupby
def remove_dups(L):
return [k for k,g in groupby(L)]
L2 = remove_dups([1,2,2,3,3,3,4,5,1,1,1])
Output: [1, 2, 3, 4, 5, 1]
Hello I just want to know what is wrong in my code.
This is a problem in the book 'Think Python' which asks to write a function to return True is the list has any duplicate elements, or False otherwise.
def has_duplicates(t):
for i in t:
if i in t.pop(t.index(i)):
return True
return False
What's wrong with it?
You remove elements from t while iterating over it. This prevents the iteration working as you'd expect, generally the effect is to skip elements. Do not do this.
t.pop(t.index(i)) returns i (or a value equal to it), so whatever you're hoping to achieve by if i in, I don't think you will achieve.
You can test it by comparing the length of the list to the length of the set created from that list, because set removes duplicates.
def has_duplicates(t):
return len(t) > len(set(t))
def has_duplicates(t):
no_dup = list(set(t))
if len(t) > len(no_dup):
return True
return False
First of all, never tamper with a list while iterating on it..
Second, your code is checking if i == the item you're popping. That will always be true since when you're popping an item python returns you that item. So it's no use to compare it with i, because you have just popped i. It's like comparing if i == i..
Try your function for t = ['1', '2', '3'] and use a debugger to verify what I'm saying..
You could do the following since you can't use sets:
def has_duplicates(t):
#first have a backup to keep the iteration clean
t_backup = t.copy()
for i in t_backup:
#first pop the item
t.pop(t.index(i))
#then check if such item still exists
if i in t:
return True
return False
Well, with list comprehension (thanks to Steve Jessop):
def has_duplicates(t):
return any(t.count(i)>1 for i in t)
lst = [1, 2, 3, 4, 5, 6, 1, 5 ,6]
print(has_duplicates(lst))
result:
>>>
True
>>>
Why does this function result in:
Oops, try again. remove_duplicates([]) resulted in an error: list index out of range"?
my function is as follows
def remove_duplicates(listIn):
listSorted = sorted(listIn)
prevInt = listSorted[0]
listOut = []
listOut.append(prevInt)
for x in listSorted:
if (x != prevInt):
prevInt = x
listOut.append(x)
print listOut
return listOut
remove_duplicates([1,2,3,3,3])
which outputs:
[1, 2, 3]
None
Thank you.
to answer your question you need to just check for the length of your list and return if its empty
if not listIn:
return []
however your whole approach could be simplified with a set like so
def remove_duplicates(listIn):
return list(set(listIn))
output:
>>> print remove_duplicates([1,2,3,3,3])
>>> [1, 2, 3]
of course this is assuming you want to keep your data in a list.. if you dont care then remove the outer list() conversion to make it faster. regardless this will be a much faster approach than what you have written
I would like to search for numbers in existing list. If is one of this numbers repeated then set variable's value to true and break for loop.
list = [3, 5, 3] //numbers in list
So if the function gets two same numbers then break for - in this case there is 3 repeated.
How to do that?
First, don't name your list list. That is a Python built-in, and using it as a variable name can give undesired side effects. Let's call it L instead.
You can solve your problem by comparing the list to a set version of itself.
Edit: You want true when there is a repeat, not the other way around. Code edited.
def testlist(L):
return sorted(set(L)) != sorted(L)
You could look into sets. You loop through your list, and either add the number to a support set, or break out the loop.
>>> l = [3, 5, 3]
>>> s = set()
>>> s
set([])
>>> for x in l:
... if x not in s:
... s.add(x)
... else:
... break
You could also take a step further and make a function out of this code, returning the first duplicated number you find (or None if the list doesn't contain duplicates):
def get_first_duplicate(l):
s = set()
for x in l:
if x not in s:
s.add(x)
else:
return x
get_first_duplicate([3, 5, 3])
# returns 3
Otherwise, if you want to get a boolean answer to the question "does this list contain duplicates?", you can return it instead of the duplicate element:
def has_duplicates(l):
s = set()
for x in l:
if x not in s:
s.add(x)
else:
return true
return false
get_first_duplicate([3, 5, 3])
# returns True
senderle pointed out:
there's an idiom that people sometimes use to compress this logic into a couple of lines. I don't necessarily recommend it, but it's worth knowing:
s = set(); has_dupe = any(x in s or s.add(x) for x in l)
you can use collections.Counter() and any():
>>> lis=[3,5,3]
>>> c=Counter(lis)
>>> any(x>1 for x in c.values()) # True means yes some value is repeated
True
>>> lis=range(10)
>>> c=Counter(lis)
>>> any(x>1 for x in c.values()) # False means all values only appeared once
False
or use sets and match lengths:
In [5]: lis=[3,3,5]
In [6]: not (len(lis)==len(set(lis)))
Out[6]: True
In [7]: lis=range(10)
In [8]: not (len(lis)==len(set(lis)))
Out[8]: False
You should never give the name list to a variable - list is a type in Python, and you can give yourself all kinds of problems masking built-in names like that. Give it a descriptive name, like numbers.
That said ... you could use a set to keep track of which numbers you've already seen:
def first_double(seq):
"""Return the first item in seq that appears twice."""
found = set()
for item in seq:
if item in found:
return item
# return will terminate the function, so no need for 'break'.
else:
found.add(item)
numbers = [3, 5, 3]
number = first_double(numbers)
without additional memory:
any(l.count(x) > 1 for x in l)