I am trying to write a function called find_it(seq) that, given list of numbers, returns the number that appears an odd amount of times.
I have tried rearranging the return and for loop. and tried without the else clause.
can someone point out how to format it? thanks
def find_it(seq):
#return i for i in seq if seq.count(i) % 2 == 1 else 0
for i in seq: return i if seq.count(i) % 2 == 1 else: pass
#this is my solution without the one line and without using count()
def find_it(seq):
dic = {}
for i in seq:
if i not in dic:
dic.update({i:1})
else:
dic[i] = 1
print(dic)
for item,num in dic.items():
if num % 2 == 1:
return item
CodePudding user response:
If you insist on making one-liner loop I suggest you use generator with next
, this will make the code more readable
def find_it(seq):
return next((i for i in seq if seq.count(i) % 2 == 1), None)
However the more efficient way will be a simple loop
def find_it(seq):
for i in seq:
if seq.count(i) % 2 == 1:
return i
CodePudding user response:
def find_it(seq):
return set([el for el in seq if seq.count(el) % 2 == 1])
> print(find_it([1, 2, 3, 1, 1, 2, 3]))
{1}
This snippet returns a set of elements present an odd number of times in a list.
It's not as efficient as can be, as checked elements present multiple times are still counted. For example, count(2) returns an int of how many 2
s are in the list, but because of how the loop is, the next time the program sees a 2
, it stills calculates the .count
of 2
even though it's done it before.
This can be rectified by removing all the occurrences of an element from the list after it's been checked, or ignore checked elements. I was unable to find a way to do this in one line as you requested.
CodePudding user response:
I digress only because OP seems intent on efficiency.
For this problem, the technique used can be influenced by the data being processed. This is best explained by example. Here are six different ways to achieve the same objective.
from collections import Counter
from timeit import timeit
# even number of 1s, odd number of 2s
list_ = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2]
def find_it_1(seq):
for k, v in Counter(seq).items():
if v % 2:
return k
def find_it_2(seq):
for i in seq:
if seq.count(i) % 2 :
return i
def find_it_3(seq):
s = set()
for e in seq:
if e not in s:
if seq.count(e) % 2:
return e
s.add(e)
def find_it_4(seq):
return next((i for i in seq if seq.count(i) % 2), None)
def find_it_5(seq):
for e in set(seq):
if seq.count(e) % 2:
return e
def find_it_6(seq):
d = {}
for e in seq:
d[e] = d.get(e, 0) 1
for k, v in d.items():
if v % 2:
return k
for func in find_it_1, find_it_2, find_it_3, find_it_4, find_it_5, find_it_6:
print(func.__name__, timeit(lambda: func(list_)))
Output:
find_it_1 1.627880711999751
find_it_2 2.23142556699986
find_it_3 0.9605982989996846
find_it_4 2.4646536830000514
find_it_5 0.6783656980001069
find_it_6 1.9190425920000962
Now, let's change the data as follows:
list_ = [2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
Note that there are 3 occurrences of 2 and that they're at the start of the list. This results in:
find_it_1 1.574513012999887
find_it_2 0.3627374699999564
find_it_3 0.4003442379998887
find_it_4 0.5936855530007961
find_it_5 0.674294768999971
find_it_6 1.8698847380001098
Quod Erat Demonstrandum