├── .gitignore ├── LICENSE ├── README.md ├── examples └── StringMap │ └── StringMap.ino ├── library.json ├── library.properties └── src └── SimpleMap.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Stefan Kremser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleMap 2 | Associative arrays with Arduino! 3 | 4 | Nothing big, just my own implementation of an associative array - also known as Map or HashMap (when you're coming from Java) for all kind of Arduino projects. 5 | 6 | I wrote it to use in my Arduino projects and to learn more about datastructures and C++. 7 | 8 | ## Installation 9 | 10 | 1) [Download](https://github.com/spacehuhn/SimpleMap/archive/master.zip) the source code from GitHub. 11 | 2) Unzip and rename the Folder name to "SimpleMap". 12 | 3) Paste it in your Library folder (Usually located somewhere at documents/Arduino/libraries). 13 | 4) Restart the Arduino IDE. 14 | 15 | You can also just download the [SimpleMap.h](https://github.com/spacehuhn/SimpleMap/blob/master/src/SimpleMap.h) 16 | file and paste it in your Arduino sketch folder. 17 | 18 | ## Usage 19 | 20 | ### Include the library 21 | ```c++ 22 | #include 23 | ``` 24 | 25 | ### Creating a map 26 | ```c++ 27 | // A map of strings 28 | // Please not that it's necessary to provide a compare function, because it's needed to keep the map sorted! 29 | SimpleMap *myMap = new SimpleMap([](String &a, String &b) -> int { 30 | if (a == b) return 0; // a and b are equal 31 | else if (a > b) return 1; // a is bigger than b 32 | else return -1; // a is smaller than b 33 | }); 34 | ``` 35 | 36 | ### Getting the map size 37 | ```c++ 38 | int theSize = myMap->size(); 39 | ``` 40 | 41 | ### Adding elements 42 | ```c++ 43 | myMap->put("one", "1"); 44 | myMap->put("two", "2"); 45 | myMap->put("three", "3"); 46 | ``` 47 | 48 | ### Getting elements 49 | ```c++ 50 | // get value of element with key "one" 51 | String one = myMap->get("one"); 52 | 53 | // get index of element with key "one" 54 | int i = myMap->getIndex("one"); 55 | 56 | // get key of element at index i 57 | String oneKey = myMap->getKey(i); 58 | 59 | // get value of element at index i 60 | String oneValue = myMap->getData(i); 61 | 62 | // check if element is in list 63 | bool hasOne = myMap->has("one"); 64 | ``` 65 | 66 | ### Replacing elements 67 | ```c++ 68 | // to replace or change a value is the same operation as to add value 69 | myMap->put("one", "1"); // adds element with key "one" and value "1" 70 | myMap->put("one", "11"); // sets the value of element with key "one" to "11" 71 | ``` 72 | 73 | ### Removing elements 74 | ```c++ 75 | // remove element with key "one" 76 | myMap->remove("one"); 77 | 78 | // remove element at index 0 79 | myMap->remove(0); 80 | 81 | // clear() will erase the entire map, leaving it with 0 elements 82 | myMap->clear(); 83 | 84 | // Please note that clear() wont free memory from pointers, you have to manually delete/free those! 85 | // Example: 86 | while(myMap->size() > 0){ 87 | delete myMap->get(0).somePointer; 88 | myMap->remove(0); 89 | } 90 | ``` 91 | 92 | ### Locking the map 93 | ```c++ 94 | // disables to remove() and put() fore new elements (changing values is allowed!) 95 | myMap->lock(); 96 | 97 | // Please note that clear() will unlock the map again! A locked map does not protect against clear() 98 | 99 | // allow put() and remove() again 100 | myMap->unlock(); 101 | 102 | // ask if map is locked 103 | bool isLocked = myMap->isLocked(); 104 | ``` 105 | -------------------------------------------------------------------------------- /examples/StringMap/StringMap.ino: -------------------------------------------------------------------------------- 1 | /* 2 | =========================================== 3 | Copyright (c) 2018 Stefan Kremser 4 | github.com/spacehuhn 5 | =========================================== 6 | */ 7 | 8 | #include 9 | 10 | SimpleMap* myMap; 11 | 12 | String toJSON() { 13 | String s; 14 | 15 | s += '{'; 16 | 17 | for (int i = 0; i < myMap->size(); i++) { 18 | s += "\"" + myMap->getKey(i) + "\":\"" + myMap->getData(i) + "\""; 19 | 20 | if (i < myMap->size() - 1) s += ','; 21 | } 22 | s += "}"; 23 | return s; 24 | } 25 | 26 | void setup() { 27 | Serial.begin(115200); 28 | delay(200); 29 | Serial.println("STARTED!"); 30 | 31 | // create a map 32 | myMap = new SimpleMap([](String& a, String& b) -> int { 33 | if (a == b) return 0; 34 | 35 | if (a > b) return 1; 36 | 37 | /*if (a < b) */ return -1; 38 | }); 39 | 40 | // adding elements 41 | myMap->put("one", "1"); 42 | myMap->put("two", "2"); 43 | myMap->put("three", "3"); 44 | 45 | Serial.println(toJSON()); 46 | 47 | // getting elements 48 | Serial.println("\"one\": " + myMap->get("one")); 49 | Serial.println("\"two\": " + myMap->get("two")); 50 | Serial.println("\"three\": " + myMap->get("three")); 51 | Serial.println("\"four\": " + myMap->get("four")); 52 | 53 | // getting index of elements 54 | Serial.println("index of \"one\": " + (String)myMap->getIndex("one")); 55 | Serial.println("index of \"two\": " + (String)myMap->getIndex("two")); 56 | Serial.println("index of \"three\": " + (String)myMap->getIndex("three")); 57 | Serial.println("index of \"four\": " + (String)myMap->getIndex("four")); 58 | 59 | // changing element values 60 | myMap->put("three", "33"); 61 | 62 | Serial.println(toJSON()); 63 | Serial.println(); 64 | 65 | // getting elements 66 | Serial.println("\"one\": " + myMap->get("one")); 67 | Serial.println("\"two\": " + myMap->get("two")); 68 | Serial.println("\"three\": " + myMap->get("three")); 69 | Serial.println("\"four\": " + myMap->get("four")); 70 | 71 | // check if elements in map 72 | Serial.println("has \"three\": " + (String)myMap->has("three")); 73 | Serial.println("has \"vier\": " + (String)myMap->has("vier")); 74 | 75 | // remove an element 76 | myMap->remove("three"); 77 | 78 | // check if elements in map 79 | Serial.println("has \"three\": " + (String)myMap->has("three")); 80 | Serial.println("has \"vier\": " + (String)myMap->has("vier")); 81 | 82 | Serial.println(toJSON()); 83 | myMap->remove(1); // delete element at index 1 (1 = index of elemt with key "two") 84 | Serial.println(toJSON()); 85 | 86 | // lock map 87 | myMap->lock(); 88 | myMap->put("four", "4"); 89 | Serial.println(toJSON()); 90 | Serial.println("Locked: " + (String)myMap->isLocked()); 91 | 92 | // unlock map 93 | myMap->unlock(); 94 | myMap->put("four", "4"); 95 | Serial.println(toJSON()); 96 | Serial.println("Locked: " + (String)myMap->isLocked()); 97 | } 98 | 99 | void loop() { 100 | // put your main code here, to run repeatedly: 101 | } -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SimpleMap", 3 | "keywords": "simple,map,associative,array,spacehuhn", 4 | "description": "Associative arrays with Arduino!", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/spacehuhn/SimpleMap.git" 9 | }, 10 | "frameworks": "arduino", 11 | "platforms": "*" 12 | } 13 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SimpleMap 2 | version=1.0.0 3 | author=Stefan Kremser 4 | maintainer=Stefan Kremser 5 | sentence=Associative arrays with Arduino! 6 | paragraph=For projects that need a quick way to use maps (or associative arrays) for all kind of data types. 7 | category=Data Processing 8 | url=https://github.com/spacehuhn/SimpleMap 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/SimpleMap.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleMap_h 2 | #define SimpleMap_h 3 | 4 | #include 5 | #include 6 | 7 | template 8 | struct SimpleMapNode { 9 | T key; 10 | U data; 11 | SimpleMapNode* next; 12 | }; 13 | 14 | template 15 | class SimpleMap { 16 | public: 17 | int (*compare)(T & a, T & b); 18 | 19 | SimpleMap(int (*compare)(T & a, T & b)); 20 | virtual ~SimpleMap(); 21 | 22 | virtual int size(); 23 | virtual void clear(); 24 | virtual void remove(T key); 25 | virtual void remove(int i); 26 | virtual void put(T key, U obj); 27 | virtual U get(T key); 28 | virtual T getKey(int i); 29 | virtual U getData(int i); 30 | virtual int getIndex(T key); 31 | virtual bool has(T key); 32 | 33 | virtual void lock(); 34 | virtual void unlock(); 35 | virtual bool isLocked(); 36 | 37 | protected: 38 | int listSize; 39 | SimpleMapNode* listBegin; 40 | SimpleMapNode* listEnd; 41 | 42 | bool locked = false; 43 | 44 | // Helps get() method by saving last position 45 | SimpleMapNode* lastNodeGot = NULL; 46 | int lastIndexGot = -1; 47 | bool isCached = false; 48 | 49 | virtual SimpleMapNode* getNode(T key); 50 | virtual SimpleMapNode* getNodeIndex(int index); 51 | }; 52 | 53 | template 54 | SimpleMap::SimpleMap(int (*compare)(T & a, T & b)) { 55 | SimpleMap::compare = compare; 56 | listBegin = NULL; 57 | listEnd = NULL; 58 | listSize = 0; 59 | isCached = false; 60 | lastIndexGot = -1; 61 | lastNodeGot = NULL; 62 | } 63 | 64 | // Clear Nodes and free Memory 65 | template 66 | SimpleMap::~SimpleMap() { 67 | clear(); 68 | } 69 | 70 | template 71 | U SimpleMap::get(T key) { 72 | SimpleMapNode* h = getNode(key); 73 | return h ? h->data : U(); 74 | } 75 | 76 | template 77 | SimpleMapNode* SimpleMap::getNode(T key) { 78 | if (listSize > 0) { 79 | if ((compare(key, listBegin->key) < 0) || (compare(key, listEnd->key) > 0)) return NULL; 80 | 81 | if (isCached) { 82 | if (compare(key, lastNodeGot->key) == 0) return lastNodeGot; 83 | } 84 | 85 | SimpleMapNode* h = listBegin; 86 | 87 | int lowerEnd = 0; 88 | int upperEnd = listSize - 1; 89 | int res; 90 | int mid = (lowerEnd + upperEnd) / 2; 91 | 92 | int hIndex = 0; 93 | 94 | while (lowerEnd <= upperEnd) { 95 | h = lastNodeGot; 96 | hIndex = lastIndexGot; 97 | 98 | res = compare(key, getNodeIndex(mid)->key); 99 | 100 | if (res == 0) { 101 | return lastNodeGot; 102 | } else if (res < 0) { 103 | // when going left, set cached node back to previous cached node 104 | lastNodeGot = h; 105 | lastIndexGot = hIndex; 106 | isCached = true; 107 | 108 | upperEnd = mid - 1; 109 | mid = (lowerEnd + upperEnd) / 2; 110 | } else if (res > 0) { 111 | lowerEnd = mid + 1; 112 | mid = (lowerEnd + upperEnd) / 2; 113 | } 114 | } 115 | } 116 | return NULL; 117 | } 118 | 119 | template 120 | SimpleMapNode* SimpleMap::getNodeIndex(int index) { 121 | if ((index < 0) || (index >= listSize)) { 122 | return NULL; 123 | } 124 | 125 | SimpleMapNode* hNode = listBegin; 126 | int c = 0; 127 | 128 | if (isCached && (index >= lastIndexGot)) { 129 | c = lastIndexGot; 130 | hNode = lastNodeGot; 131 | } 132 | 133 | while (hNode != NULL && c < index) { 134 | hNode = hNode->next; 135 | c++; 136 | } 137 | 138 | if (hNode) { 139 | isCached = true; 140 | lastIndexGot = c; 141 | lastNodeGot = hNode; 142 | } 143 | 144 | return hNode; 145 | } 146 | 147 | template 148 | void SimpleMap::clear() { 149 | unlock(); 150 | 151 | SimpleMapNode* h = listBegin; 152 | SimpleMapNode* toDelete; 153 | 154 | while (h != NULL) { 155 | toDelete = h; 156 | h = h->next; 157 | delete toDelete; 158 | } 159 | 160 | listBegin = NULL; 161 | listEnd = NULL; 162 | listSize = 0; 163 | 164 | isCached = false; 165 | lastIndexGot = -1; 166 | lastNodeGot = NULL; 167 | } 168 | 169 | template 170 | int SimpleMap::size() { 171 | return listSize; 172 | } 173 | 174 | template 175 | void SimpleMap::put(T key, U obj) { 176 | // create new node 177 | SimpleMapNode* newNode = new SimpleMapNode(); 178 | newNode->next = NULL; 179 | newNode->data = obj; 180 | newNode->key = key; 181 | 182 | // look if already in list 183 | SimpleMapNode* h = listBegin; 184 | SimpleMapNode* p = NULL; 185 | bool found = false; 186 | int c = 0; 187 | 188 | if (listSize > 0) { 189 | while (h != NULL && !found) { 190 | if (compare(h->key, key) == 0) { 191 | found = true; 192 | } else { 193 | p = h; 194 | h = h->next; 195 | c++; 196 | } 197 | } 198 | } 199 | 200 | // replace old node with new one 201 | if (found) { 202 | if (h == listBegin) listBegin = newNode; 203 | 204 | if (h == listEnd) listEnd = newNode; 205 | 206 | if (p) p->next = newNode; 207 | newNode->next = h->next; 208 | delete h; 209 | 210 | lastIndexGot = c; 211 | lastNodeGot = newNode; 212 | isCached = true; 213 | } 214 | 215 | // create new node 216 | else if (!locked) { 217 | if (listSize == 0) { 218 | // add at start (first node) 219 | listBegin = newNode; 220 | listEnd = newNode; 221 | 222 | lastIndexGot = 0; 223 | } else { 224 | if (compare(key, listEnd->key) >= 0) { 225 | // add at end 226 | listEnd->next = newNode; 227 | listEnd = newNode; 228 | 229 | lastIndexGot = listSize; 230 | } else if (compare(key, listBegin->key) <= 0) { 231 | // add at start 232 | newNode->next = listBegin; 233 | listBegin = newNode; 234 | 235 | lastIndexGot = 0; 236 | } else { 237 | // insertion sort 238 | h = listBegin; 239 | p = NULL; 240 | found = false; 241 | c = 0; 242 | 243 | while (h != NULL && !found) { 244 | if (compare(h->key, key) > 0) { 245 | found = true; 246 | } else { 247 | p = h; 248 | h = h->next; 249 | c++; 250 | } 251 | } 252 | newNode->next = h; 253 | 254 | if (p) p->next = newNode; 255 | 256 | lastIndexGot = c; 257 | } 258 | } 259 | 260 | listSize++; 261 | 262 | isCached = true; 263 | lastNodeGot = newNode; 264 | } 265 | } 266 | 267 | template 268 | void SimpleMap::remove(T key) { 269 | if ((listSize > 0) && !locked) { 270 | if ((compare(key, listBegin->key) < 0) || (compare(key, listEnd->key) > 0)) return; 271 | 272 | SimpleMapNode* h = listBegin; 273 | SimpleMapNode* p = NULL; 274 | bool found = false; 275 | 276 | while (h != NULL && !found) { 277 | if (h->key == key) { 278 | found = true; 279 | } else { 280 | p = h; 281 | h = h->next; 282 | } 283 | } 284 | 285 | if (found) { 286 | if (p) p->next = h->next; 287 | else listBegin = h->next; 288 | 289 | if (listEnd == h) listEnd = p; 290 | listSize--; 291 | delete h; 292 | } 293 | } 294 | } 295 | 296 | template 297 | void SimpleMap::remove(int i) { 298 | if (listSize > 0) { 299 | SimpleMapNode* h = getNodeIndex(i); 300 | 301 | if (h != NULL) { 302 | SimpleMapNode* p = getNodeIndex(i - 1); 303 | 304 | if (p != NULL) p->next = h->next; 305 | 306 | if (h == listBegin) listBegin = h->next; 307 | 308 | if (h == listEnd) listEnd = p; 309 | 310 | listSize--; 311 | delete h; 312 | } 313 | } 314 | } 315 | 316 | template 317 | bool SimpleMap::has(T key) { 318 | return getNode(key) != NULL; 319 | } 320 | 321 | template 322 | T SimpleMap::getKey(int i) { 323 | SimpleMapNode* h = getNodeIndex(i); 324 | return h ? h->key : T(); 325 | } 326 | 327 | template 328 | U SimpleMap::getData(int i) { 329 | SimpleMapNode* h = getNodeIndex(i); 330 | return h ? h->data : U(); 331 | } 332 | 333 | template 334 | int SimpleMap::getIndex(T key) { 335 | return getNode(key) ? lastIndexGot : -1; 336 | } 337 | 338 | template 339 | void SimpleMap::lock() { 340 | locked = true; 341 | } 342 | 343 | template 344 | void SimpleMap::unlock() { 345 | locked = false; 346 | } 347 | 348 | template 349 | bool SimpleMap::isLocked() { 350 | return locked; 351 | } 352 | 353 | #endif // ifndef SimpleMap_h --------------------------------------------------------------------------------