Skip to content

Commit 903dd56

Browse files
committed
Add HashMap
1 parent f499d1b commit 903dd56

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import 'package:test/test.dart';
2+
3+
/// Note: Hash Tables (Hash Maps) can be implemented as linear or non-linear data structure.
4+
/// Often, they are implemented as a linear data structure using arrays.
5+
///
6+
/// Dart's different built-in hash maps and the default one:
7+
/// https://stackoverflow.com/questions/49224999/does-dart-automatically-determine-which-map-type-to-use
8+
9+
/// The prime number in the hash is helpful in spreading out the keys more uniformly.
10+
/// It's also helpful if the array that you're putting values into has a prime length.
11+
/// You don't need to know why, but here are some sources:
12+
/// https://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers
13+
/// https://www.quora.com/Does-making-array-size-a-prime-number-help-in-hash-table-implementation-Why
14+
const _mapLength = 53;
15+
16+
/// Internal class to store each key-value pair
17+
class Entry<K, V> {
18+
Entry(this.key, this.value);
19+
20+
final K key;
21+
V value;
22+
}
23+
24+
class HashMap<K, V> {
25+
/// Internal list to store the keys and values via separate chaining
26+
final _table = List<List<Entry<K, V>>?>.filled(_mapLength, null);
27+
28+
/// Loops through the hash table array and returns an array of keys in the table
29+
List<K> keys() {
30+
List<K> keys = [];
31+
for (final entryList in _table) {
32+
entryList?.forEach((entry) {
33+
keys.add(entry.key);
34+
});
35+
}
36+
return keys;
37+
}
38+
39+
/// Loops through the hash table array and returns an array of values in the table
40+
List<V> values() {
41+
List<V> values = [];
42+
for (final entryList in _table) {
43+
entryList?.forEach((entry) {
44+
values.add(entry.value);
45+
});
46+
}
47+
return values;
48+
}
49+
50+
/// Pseudocode:
51+
/// - Accepts a key and a value.
52+
/// - Hashes the key.
53+
/// - Stores the key-value pair in the hash table array via separate chaining.
54+
/// - Note: If the key-value pair is already there, override the value and return.
55+
void set(K key, V value) {
56+
final index = _hash(key);
57+
final entry = Entry(key, value);
58+
59+
if (_table[index] case final entryList?) {
60+
//check if any entry at that index has the same key.
61+
for (final entry in entryList) {
62+
if (entry.key == key) {
63+
entry.value = value;
64+
return;
65+
}
66+
}
67+
entryList.add(entry);
68+
} else {
69+
_table[index] = [entry];
70+
}
71+
}
72+
73+
/// Pseudocode:
74+
/// - Accepts a key.
75+
/// - Hashes the key.
76+
/// - Retrieves the key-value pair in the hash table and return the value.
77+
/// - If the key isn't found, returns null.
78+
V? get(K key) {
79+
final index = _hash(key);
80+
if (_table[index] case final entryList?) {
81+
for (final entry in entryList) {
82+
if (entry.key == key) return entry.value;
83+
}
84+
}
85+
return null;
86+
}
87+
88+
int _hash(K key) {
89+
return key.hashCode % _table.length;
90+
/*
91+
//Manual hash that works on strings only
92+
int total = 0;
93+
const prime = 31;
94+
for (int i = 0; i < math.min(key.length, 100); i++) {
95+
final value = key.codeUnitAt(i) - 96;
96+
total = ((total * prime) + value) % _table.length;
97+
}
98+
return total;
99+
*/
100+
}
101+
}
102+
103+
void main() {
104+
test('should add entries to the HashMap and retrieve with the key', () {
105+
final map = HashMap();
106+
107+
map.set(1, 'Ahmed');
108+
map.set(2, 'ElSayed');
109+
map.set('stringKey', 'Dart');
110+
111+
expect(map.get(1), 'Ahmed');
112+
expect(map.get(2), 'ElSayed');
113+
expect(map.get(3), null);
114+
expect(map.get('stringKey'), 'Dart');
115+
});
116+
117+
test('should override the value when adding entry with an existing key', () {
118+
final map = HashMap();
119+
120+
map.set(1, 'Ahmed');
121+
expect(map.get(1), 'Ahmed');
122+
123+
map.set(1, 'ElSayed');
124+
expect(map.get(1), 'ElSayed');
125+
expect(map.keys(), [1]);
126+
expect(map.values(), ['ElSayed']);
127+
});
128+
129+
test('should retrieve all keys/values of the map', () {
130+
final map = HashMap();
131+
132+
map.set(1, 'Ahmed');
133+
map.set(2, 'ElSayed');
134+
map.set(3, 3);
135+
map.set('stringKey', 'Dart');
136+
137+
expect(map.keys(), ['stringKey', 3, 2, 1]);
138+
expect(map.values(), ['Dart', 3, 'ElSayed', 'Ahmed']);
139+
});
140+
}

0 commit comments

Comments
 (0)