Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A std::map that keep track of the order of insertion?

People also ask

Does C++ map maintain insertion order?

C++ hash map and hash set which preserves the order of insertion. The ordered-map library provides a hash map and a hash set which preserve the order of insertion in a way similar to Python's OrderedDict. When iterating over the map, the values will be returned in the same order as they were inserted.

Does unordered map preserve insertion order?

No, it is not possible. Usage of std::unordered_map doesn't give you any guarantee on element order. If you want to keep elements sorted by map keys (as seems from your example) you should use std::map .

Is std::map in order?

std::map is a key-value container that maintains its keys in sorted order at all times.


If you have only 50 values in std::map you could copy them to std::vector before printing out and sort via std::sort using appropriate functor.

Or you could use boost::multi_index. It allows to use several indexes. In your case it could look like the following:

struct value_t {
      string s;
      int    i;
};

struct string_tag {};

typedef multi_index_container<
    value_t,
    indexed_by<
        random_access<>, // this index represents insertion order
        hashed_unique< tag<string_tag>, member<value_t, string, &value_t::s> >
    >
> values_t;

You might combine a std::vector with a std::tr1::unordered_map (a hash table). Here's a link to Boost's documentation for unordered_map. You can use the vector to keep track of the insertion order and the hash table to do the frequent lookups. If you're doing hundreds of thousands of lookups, the difference between O(log n) lookup for std::map and O(1) for a hash table might be significant.

std::vector<std::string> insertOrder;
std::tr1::unordered_map<std::string, long> myTable;

// Initialize the hash table and record insert order.
myTable["foo"] = 0;
insertOrder.push_back("foo");
myTable["bar"] = 0;
insertOrder.push_back("bar");
myTable["baz"] = 0;
insertOrder.push_back("baz");

/* Increment things in myTable 100000 times */

// Print the final results.
for (int i = 0; i < insertOrder.size(); ++i)
{
    const std::string &s = insertOrder[i];
    std::cout << s << ' ' << myTable[s] << '\n';
}

Tessil has a very nice implementaion of ordered map (and set) which is MIT license. You can find it here: ordered-map

Map example

#include <iostream>
#include <string>
#include <cstdlib>
#include "ordered_map.h"

int main() {
tsl::ordered_map<char, int> map = {{'d', 1}, {'a', 2}, {'g', 3}};
map.insert({'b', 4});
map['h'] = 5;
map['e'] = 6;

map.erase('a');


// {d, 1} {g, 3} {b, 4} {h, 5} {e, 6}
for(const auto& key_value : map) {
    std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl;
}


map.unordered_erase('b');

// Break order: {d, 1} {g, 3} {e, 6} {h, 5}
for(const auto& key_value : map) {
    std::cout << "{" << key_value.first << ", " << key_value.second << "}" << std::endl;
}
}

Keep a parallel list<string> insertionOrder.

When it is time to print, iterate on the list and do lookups into the map.

each element in insertionOrder  // walks in insertionOrder..
    print map[ element ].second // but lookup is in map

If you need both lookup strategies, you will end up with two containers. You may use a vector with your actual values (ints), and put a map< string, vector< T >::difference_type> next to it, returning the index into the vector.

To complete all that, you may encapsulate both in one class.

But I believe boost has a container with multiple indices.