Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ STL map I don't want it to sort!

Tags:

c++

stl

People also ask

How do you stop a map from sorting?

The way map is implemented internally, it's not possible for you to prevent keys to be sorted. However, you can maintain an additional list(using vector) to store the order in which keys appear. Later on, iterate over the map and the vector to achieve what you want.

Are STL maps sorted?

No. It will iterate based on the sorted order, not the order that you inserted elements. In the case of std::string , it sorts in lexicographic order (alphabetic order).

Is std::map always sorted?

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

Are std::map keys sorted?

std::map is a sorted associative container that contains key-value pairs with unique keys. Keys are sorted by using the comparison function Compare .


There is no standard container that does directly what you want. The obvious container to use if you want to maintain insertion order is a vector. If you also need look up by string, use a vector AND a map. The map would in general be of string to vector index, but as your data is already integers you might just want to duplicate it, depending on your use case.


Like Matthieu has said in another answer, the Boost.MultiIndex library seems the right choice for what you want. However, this library can be a little tough to use at the beginning especially if you don't have a lot of experience with C++. Here is how you would use the library to solve the exact problem in the code of your question:

struct person {
    std::string name;
    int id;
    person(std::string const & name, int id) 
    : name(name), id(id) { 
    }
};

int main() {

    using namespace::boost::multi_index;
    using namespace std;

    // define a multi_index_container with a list-like index and an ordered index
    typedef multi_index_container<
      person,        // The type of the elements stored
      indexed_by<    // The indices that our container will support
        sequenced<>,                           // list-like index
        ordered_unique<member<person, string, 
                              &person::name> > // map-like index (sorted by name)
      >
    > person_container;

    // Create our container and add some people
    person_container persons;
    persons.push_back(person("B", 123));
    persons.push_back(person("C", 224));
    persons.push_back(person("A", 321));

    // Typedefs for the sequence index and the ordered index
    enum { Seq, Ord };
    typedef person_container::nth_index<Seq>::type persons_seq_index;
    typedef person_container::nth_index<Ord>::type persons_ord_index;

    // Let's test the sequence index
    persons_seq_index & seq_index = persons.get<Seq>();
    for(persons_seq_index::iterator it = seq_index.begin(), 
                                    e = seq_index.end(); it != e; ++it)
        cout << it->name << ":"<< it->id << endl;
    cout << "\n";

    // And now the ordered index
    persons_ord_index & ord_index = persons.get<Ord>();
    for(persons_ord_index::iterator it = ord_index.begin(), 
                                    e = ord_index.end(); it != e; ++it)
        cout << it->name << ":"<< it->id << endl;
    cout << "\n";

    // Thanks to the ordered index we have fast lookup by name:
    std::cout << "The id of B is: " << ord_index.find("B")->id << "\n";
}

Which produces the following output:

B:123
C:224
A:321

A:321
B:123
C:224

The id of B is: 123

Map is definitely not right for you:

"Internally, the elements in the map are sorted from lower to higher key value following a specific strict weak ordering criterion set on construction."

Quote taken from here.

Unfortunately there is no unordered associative container in the STL, so either you use a nonassociative one like vector, or write your own :-(


I had the same problem every once in a while and here is my solution: https://github.com/nlohmann/fifo_map. It's a header-only C++11 solution and can be used as drop-in replacement for a std::map.

For your example, it can be used as follows:

#include "fifo_map.hpp"
#include <string>
#include <iostream>

using nlohmann::fifo_map;

int main()
{
    fifo_map<std::string,int> persons;

    persons["B"] = 123;
    persons["A"] = 321;

    for(fifo_map<std::string,int>::iterator i = persons.begin();
        i!=persons.end();
        ++i)
    {
        std::cout<< (*i).first << ":"<<(*i).second << std::endl;
    }
}

The output is then

B:123
A:321

Besides Neil's recommendation of a combined vector+map if you need both to keep the insertion order and the ability to search by key, you can also consider using boost multi index libraries, that provide for containers addressable in more than one way.