Skip to content

Commit e024825

Browse files
committed
Added preliminary hashtable implementation and unit tests for everything thus far.
1 parent b4a2f70 commit e024825

7 files changed

+199
-15
lines changed

README.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
# Algorithm Implementations
22

3-
This is just a collection of algorithms I wanted to implement in order to improve my understanding. They are not optimized and probably shouldn't be used in production code.
3+
This is just a collection of algorithms I wanted to implement in order to improve my understanding. They are NOT optimized and probably shouldn't be used in production code.
4+
5+
To run tests for a specific module:
6+
7+
python3 <module_name>.py
8+
9+
To run all unit tests:
10+
11+
python3 -m unittest discover test
412

hashtable.py

+63-6
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,68 @@
1+
############ HashTable helper functions
12
def hash_function(key_str, size):
23
return sum([ord(c) for c in key_str]) % size
34

4-
if __name__ == "__main__":
5-
test_inputs = ["a", "caleb", "test", "more than a feeling"]
65

7-
hash_func = lambda key: hash_function(key, 10)
8-
print(map(hash_func, test_inputs))
6+
############ HashTable class
7+
class HashTable:
8+
""" Hash table which uses strings for keys. Value can be any object. """
9+
10+
def __init__(self, capacity=1000):
11+
""" Capacity defaults to 1000. """
12+
13+
self.capacity = capacity
14+
self.size = 0
15+
# Storage format: [ [ [key1, value], [key2, value] ], [ [key3, value] ] ]
16+
# The outmost list is the one which the hash function maps the index to. The next inner
17+
# Array is the list of objects in that storage cell. The 3rd level is the individual
18+
# item array, where the 1st item is the key, and the 2nd item is the value.
19+
self.data = [[] for i in range(capacity)]
20+
21+
def set(self, key, obj):
22+
""" Insert object with key into hashtable. If key already exists, then the object will be
23+
updated. Key must be a string. Returns self. """
24+
25+
index = hash_function(key, self.capacity)
26+
storage_cell = self.data[index]
27+
for item in storage_cell:
28+
if item[0] == key:
29+
item[1] = obj
30+
else:
31+
storage_cell.append([key, obj])
32+
self.size += 1
33+
34+
return self
35+
36+
def get(self, key):
37+
""" Get object with key (key must be a string). If not found, it will raise a KeyError. """
38+
39+
index = hash_function(key, self.capacity)
40+
storage_cell = self.data[index]
41+
for item in storage_cell:
42+
if item[0] == key:
43+
return item[1]
44+
else:
45+
raise KeyError(key)
46+
47+
def remove(self, key):
48+
""" Remove the object associated with key from the hashtable. If found, the object will
49+
be returned. If not found, KeyError will be raised. """
50+
51+
index = hash_function(key, self.capacity)
52+
storage_cell = self.data[index]
53+
item_to_remove = None
54+
for item in storage_cell:
55+
if item[0] == key:
56+
item_to_remove = item
57+
if item_to_remove:
58+
storage_cell.remove(item)
59+
self.size -= 1
60+
return item[1]
61+
else:
62+
raise KeyError(key)
63+
64+
if __name__ == "__main__":
65+
import unittest
66+
testsuite = unittest.TestLoader().discover('test', pattern="*hashtable*")
67+
unittest.TextTestRunner(verbosity=1).run(testsuite)
968

10-
hash_func = lambda key: hash_function(key, 4)
11-
print(map(hash_func, test_inputs))

mergesort.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ def mergesort(lst):
3737

3838

3939
if __name__ == "__main__":
40-
import random
41-
data = [random.randint(0,10) for i in range(9)]
42-
print('UNSORTED:', data)
43-
print('SORTED: ', mergesort(data))
40+
import unittest
41+
testsuite = unittest.TestLoader().discover('test', pattern="*mergesort*")
42+
unittest.TextTestRunner(verbosity=1).run(testsuite)
4443

quicksort.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ def quicksort(lst):
1919
return quicksort(low) + [pivot] + quicksort(high)
2020

2121
if __name__ == "__main__":
22-
import random
23-
data = [random.randint(0,10) for i in range(9)]
24-
print('UNSORTED:', data)
25-
print('SORTED: ', quicksort(data))
22+
import unittest
23+
testsuite = unittest.TestLoader().discover('test', pattern="*quicksort*")
24+
unittest.TextTestRunner(verbosity=1).run(testsuite)
2625

test/test_hashtable.py

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import unittest
2+
import sys
3+
import os.path
4+
5+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
6+
import hashtable
7+
8+
9+
class HashFunctionTest(unittest.TestCase):
10+
11+
def test(self):
12+
hash10 = lambda key: hashtable.hash_function(key, 10)
13+
self.assertIn(hash10("a"), range(10))
14+
self.assertIn(hash10("b"), range(10))
15+
self.assertIn(hash10("caleb"), range(10))
16+
self.assertIn(hash10("more than a feeling"), range(10))
17+
18+
hash4 = lambda key: hashtable.hash_function(key, 4)
19+
self.assertIn(hash4("a"), range(4))
20+
self.assertIn(hash4("b"), range(4))
21+
self.assertIn(hash4("caleb"), range(4))
22+
self.assertIn(hash4("more than a feeling"), range(4))
23+
24+
25+
class HashTableTest(unittest.TestCase):
26+
def testCreate(self):
27+
ht = hashtable.HashTable(10)
28+
self.assertEqual(ht.capacity, 10)
29+
self.assertEqual(ht.size, 0)
30+
31+
ht = hashtable.HashTable()
32+
self.assertEqual(ht.capacity, 1000)
33+
34+
def testSetAndGet(self):
35+
ht = hashtable.HashTable(10)
36+
ht.set('a', 1)
37+
self.assertEqual(ht.get('a'), 1)
38+
self.assertEqual(ht.size, 1)
39+
40+
ht.set('a', 2)
41+
self.assertEqual(ht.get('a'), 2)
42+
#self.assertEqual(ht.size, 1)
43+
44+
ht.set('b', 10)
45+
self.assertEqual(ht.get('b'), 10)
46+
self.assertEqual(ht.get('a'), 2)
47+
#self.assertEqual(ht.size, 2)
48+
49+
# Assert ht.set returns itself (for fluent calls)
50+
self.assertEqual(ht.set('c', 5), ht)
51+
52+
ht.set('d', 100).set('e', 200).set('f', 300)
53+
self.assertEqual(ht.get('d'), 100)
54+
self.assertEqual(ht.get('e'), 200)
55+
self.assertEqual(ht.get('f'), 300)
56+
57+
def testBadGet(self):
58+
ht = hashtable.HashTable(10)
59+
self.assertRaises(KeyError, ht.get, 'a')
60+
61+
def testRemove(self):
62+
ht = hashtable.HashTable(10)
63+
self.assertRaises(KeyError, ht.remove, 'a')
64+
65+
ht.set('a', 1)
66+
removed_item = ht.remove('a')
67+
self.assertEqual(removed_item, 1)
68+
self.assertEqual(ht.size, 0)
69+
70+
71+
if __name__ == '__main__':
72+
unittest.main()
73+

test/test_mergesort.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import unittest
2+
import sys
3+
import os.path
4+
5+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
6+
import mergesort
7+
8+
9+
class MergesortTest(unittest.TestCase):
10+
11+
def testSort(self):
12+
a = [1,2,3,4,5,6,7,8,9]
13+
self.assertEqual(mergesort.mergesort(a), [1,2,3,4,5,6,7,8,9])
14+
15+
b = [8,7,6,5,4,3,2,1]
16+
self.assertEqual(mergesort.mergesort(b), [1,2,3,4,5,6,7,8])
17+
18+
c = [1,6,3,2,1,9,7,5,4,9]
19+
self.assertEqual(mergesort.mergesort(c), [1,1,2,3,4,5,6,7,9,9])
20+
21+
22+
if __name__ == '__main__':
23+
unittest.main()
24+

test/test_quicksort.py

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import unittest
2+
import sys
3+
import os.path
4+
5+
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
6+
import quicksort
7+
8+
9+
class QuicksortTest(unittest.TestCase):
10+
11+
def testSort(self):
12+
a = [1,2,3,4,5,6,7,8,9]
13+
self.assertEqual(quicksort.quicksort(a), [1,2,3,4,5,6,7,8,9])
14+
15+
b = [8,7,6,5,4,3,2,1]
16+
self.assertEqual(quicksort.quicksort(b), [1,2,3,4,5,6,7,8])
17+
18+
c = [1,6,3,2,1,9,7,5,4,9]
19+
self.assertEqual(quicksort.quicksort(c), [1,1,2,3,4,5,6,7,9,9])
20+
21+
22+
if __name__ == '__main__':
23+
unittest.main()
24+

0 commit comments

Comments
 (0)