/* # map http://www.cplusplus.com/reference/map/map/ Also comes in an unordered version `unordered_map`. Ordered. Also comes in an multiple value input version `multimap`. Does not require a hash function. Usually implemented as a self balancing tree such as a rb tree. # hashmap There seems to be no explicit hashmap container, only a generic map interface, However unordered_map is likely to be hashmap based. A nonstandard `hash_map` already provided with gcc and msvc++. It is placed in the `std::` namespace, but it is *not* ISO. */ #include "common.hpp" template std::string map_to_str(std::map map) { std::stringstream result; for (auto& pair : map) { result << pair.first << ":" << pair.second << ", "; } return result.str(); } int main() { /* Create initialized map. */ { std::map m{ {0, "zero"}, {1, "one"}, }; assert(m[0] == "zero"); assert(m[1] == "one"); } /* emplace Put a value pair into the map without creating the pair explicitly. Only inserts if not already present. Needs gcc 4.8: http://stackoverflow.com/questions/15812276/stdset-has-no-member-emplace */ { std::map m; assert((m.emplace(0, "zero").second)); assert((m.emplace(1, "one").second)); assert(!(m.emplace(1, "one2").second)); assert(m[0] == "zero"); assert(m[1] == "one"); } /* # operator[] get value from a given key. */ { std::map m{ {0, "zero"}, {1, "one"}, }; assert(m[0] == "zero"); assert(m[1] == "one"); // Returns a reference that can override the value. m[1] = "one2"; assert(m[1] == "one2"); // WARNING: if the key does not exist, it is inserted with a value with default constructor. // This can be avoided by using `find` instead of `[]`. // Inserts `(2,"")` because `""` is the value for the default String constructor. // http://stackoverflow.com/questions/10124679/what-happens-if-i-read-a-maps-value-where-the-key-does-not-exist { assert(m[2] == ""); assert(m.size() == 3); // This behaviour is however very convenient for nested containers. { std::map> m; // Create the empty map at m[0], and immediately add a (0,0) pair to it. m[0][0] = 0; // map at m[0] already exists, now just add a new (1, 1) pair to it. m[0][1] = 1; m[1][0] = 2; assert(m[0][0] == 0); assert(m[0][1] == 1); assert(m[1][0] == 2); } } } /* # find # check if in map Similar to `std::set` find with respect to the keys: returns an iterator pointing to the pair which has given key, not the value. If not found, returns `map::end()` This is preferable to `[]` since it does not insert non-existent elements. */ { std::map m{ {0, "zero"}, {1, "one"}, }; auto found = m.find(0); assert(found != m.end()); assert(found->second == "zero"); assert(m.find(2) == m.end()); assert(m.size() == 2); /* Get default provided value if key not present TODO: any less verbose way than finding and check != end? Like: m.get(key, default) I know I can define a helper, but come on... */ { std::map m{}; int default_ = 42; int result; auto f = m.find(1); if (f == m.end()) { result = default_; } else { result = f->second; } } } /* # insert Insert pair into map. The return value is similar to that of a set insertion with respect to the key. Just use emplace instead, less verbose as it was added after perfect forwarding. http://stackoverflow.com/questions/17172080/insert-vs-emplace-vs-operator-in-c-map */ { std::map m; std::pair::iterator,bool> ret; ret = m.insert(std::make_pair(0, "zero")); assert(ret.first == m.find(0)); assert(ret.second == true); ret = m.insert(std::make_pair(1, "one")); assert(ret.first == m.find(1)); assert(ret.second == true); //key already present ret = m.insert(std::make_pair(1, "one2")); assert(m[1] == "one"); assert(ret.first == m.find(1)); assert(ret.second == false); } /* # iterate Map is ordered: http://stackoverflow.com/questions/7648756/is-the-order-of-iterating-through-stdmap-known-and-guaranteed-by-the-standard It is iterated in key `<` order. So this basically requires implementations to use balanced trees intead of hashmap. Iteration returns key value pairs. */ { std::map m{ {1, "one"}, {0, "zero"}, }; int i = 0; int is[] = {0, 1}; for (auto& im : m) { assert(im.first == is[i]); //cout << im->second << endl; ++i; } assert(i == 2); assert(map_to_str(m) == "0:zero, 1:one, "); } /* # erase Remove element from map. */ { // key version. Returns number of elements removed (0 or 1). { std::map m{ {0, "zero"}, {1, "one"}, }; int ret; ret = m.erase(1); assert(ret = 1); assert((m == std::map{{0, "zero"}})); ret = m.erase(1); assert(ret == 0); } // iterator version. Returns iterator to next element. // Does not invalidate other iterators. // http://stackoverflow.com/questions/6438086/iterator-invalidation-rules { std::map m{ {0, "zero"}, {1, "one"}, }; auto itNext = m.find(1); auto it = m.find(0); assert(m.erase(it) == itNext); assert((m == std::map{{1, "one"}})); } } // # Range switch case. // http://stackoverflow.com/questions/9432226/how-do-i-select-a-range-of-values-in-a-switch-statement/42331563#42331563 { auto result = std::map{ {-1, -1}, { 0, 0}, { 1, 0}, { 2, 2}, { 3, 2}, { 4, 2}, { 5, 5}, { 6, 5}, { 7, 7}, }; std::map> m; int x; m.emplace(0, [&](){ x = -1; }); m.emplace(2, [&](){ x = 0; }); m.emplace(5, [&](){ x = 2; }); m.emplace(7, [&](){ x = 5; }); for (auto i = -1; i < 8; ++i) { auto it = m.upper_bound(i); if (it == m.end()) { x = 7; } else { it->second(); } assert(x == result[i]); } } }