DarinDouglas's comment is right. However, it forgets to point out that your code actually does not work (or at least it will not always work).
Before going any further, it might be interesting to write a few unit tests. Here's what I have so far (the names suck). One you have this, you can see that something is not quite right if you run the tests (python3 -m unittest intersect.py
) enough times.
class Test(unittest.TestCase):
def test_intersect(self):
self.assertIn( intersects("abc", "bc", 1), ['b', 'c'])
self.assertIn( intersects("abc", "bc", 2), ['bc'])
self.assertIsNone(intersects("abc", "bc", 3))
self.assertIn( intersects("abcd", "bcd", 1), ['b', 'c', 'd'])
self.assertIn( intersects("abcd", "bcd", 2), ['bc', 'cd'])
self.assertIn( intersects("abcd", "bcd", 3), ['bcd'])
self.assertIsNone(intersects("abcd", "bcd", 4))
Now, you should be able to see that something is wrong. Indeed, a dictionnary is an unsorted container so when you convert it to a list, you do not know for sure the order of the elements of the list. This is a big problem in your case because list(hash_sequence(string1,k))[i]
will call the function and build a new list possibly in the different order. For that reason, values might be missed.
It took me a while to spot the issue but once we know it, the fix is easy : let's call the function and build the lists only once.
def intersects(string1, string2, k):
l1 = list(hash_sequence(string1,k))
l2 = list(hash_sequence(string2,k))
itr=min(len(l1),len(l2))
for i in range(itr):
if l1[i] in l2:
return l1[i]
return None
You are not done yet because there is another bug in your case which does not always appear (depending of the order of the element in the list). Indeed, the idea is to check for each element in l1
if it is in l2
. This is NOT what you are doing at the moment. Indeed, if l2
is shorter than l1
, you won't check every element of l1
. Once this is understood, the bug is quite easy to fix.
def intersects(string1, string2, k):
l1 = list(hash_sequence(string1,k))
l2 = list(hash_sequence(string2,k))
for i in range(len(l1)):
if l1[i] in l2:
return l1[i]
return None
This seems to be working better but can be improved. Indeed, the pythonic way to loop over containers is not to used indices.
You can make your code safer, faster, clearer and shorter by writing :
def intersects(string1, string2, k):
l1 = list(hash_sequence(string1,k))
l2 = list(hash_sequence(string2,k))
for e in l1:
if e in l2:
return e
return None
Now, let's have a look at hash_sequence
. First thing I noticed is that it has a quite confusing name as it can make one think that it will be about computing hashes. Also, it returns a dictionnary for no obvious reasons : a set would probably do the trick here :
def get_chunks(string, length):
s=set()
for i in range(len(string)-(length-1)):
chunk = string[i:i+length]
s.add(chunk)
return s
This being done, my unit tests are still passing which gives me some confidence that everything's allright. I can now go back to intersects
and improve it : we don't really need to convert the sets to lists : the in
is faster on sets.
def intersects(string1, string2, k):
s1 = get_chunks(string1,k)
s2 = get_chunks(string2,k)
for e in s1:
if e in s2:
return e
return None
Now, you could make your function a bit more powerful (and a bit slower) by returning all common chunks and not just one. This could be easily done using the buillt-in operations on set (return get_chunks(string1,k) & get_chunks(string2,k)
).
A final thing (a few other things could be said but I am running out of time and it might be too much for the time being) : if you want to pretend you are a cool kid, you can use set comprehension in your get_chunks
function. Then it becomes the concise :
def get_chunks(string, length):
return set(string[i:i+length] for i in range(len(string)-(length-1)))
The whole code becomes :
import unittest
def get_chunks(string, length):
return set(string[i:i+length] for i in range(len(string)-(length-1)))
def intersects(string1, string2, k):
s1 = get_chunks(string1,k)
s2 = get_chunks(string2,k)
for e in s1:
if e in s2:
return e
return None
class Test(unittest.TestCase):
def test_intersect(self):
self.assertIn( intersects("abc", "bc", 1), ['b', 'c'])
self.assertIn( intersects("abc", "bc", 2), ['bc'])
self.assertIsNone(intersects("abc", "bc", 3))
self.assertIn( intersects("abcd", "bcd", 1), ['b', 'c', 'd'])
self.assertIn( intersects("abcd", "bcd", 2), ['bc', 'cd'])
self.assertIn( intersects("abcd", "bcd", 3), ['bcd'])
self.assertIsNone(intersects("abcd", "bcd", 4))
self.assertIn( intersects("bc", "abc", 1), ['b', 'c'])
self.assertIn( intersects("bc", "abc", 2), ['bc'])
self.assertIsNone(intersects("bc", "abc", 3))
self.assertIn( intersects("bcd", "abcd", 1), ['b', 'c', 'd'])
self.assertIn( intersects("bcd", "abcd", 2), ['bc', 'cd'])
self.assertIn( intersects("bcd", "abcd", 3), ['bcd'])
self.assertIsNone(intersects("bcd", "abcd", 4))