Question

Mapping a record type to a vector of field values:

unordered_map<string, vector<string>> input_records;
string rec_type {"name"};
vector<string> fields { "field 1", "field 2", "field 3" };

I want to copy fields to input_records[rec_type]. The following doesn't work:

_input_records[rec_type].insert(rec_deq.begin(), rec_deq.end()); 

Yet the boost documentation for unordered_map contains this:

template<typename InputIterator> 
void insert(InputIterator first, InputIterator last);

Inserts a range of elements into the container. Elements are inserted if and only if there is no element in the container with an equivalent key.

Throws:
When inserting a single element, if an exception is thrown by an operation other than a call to hasher the function has no effect.
Notes:
Can invalidate iterators, but only if the insert causes the load factor to be greater to or equal to the maximum load factor.
Pointers and references to elements are never invalidated.

(Writing this, I realize that probably the InputIterator points to a sequence of key/value pairs, each of which is to be inserted into the Map. So, wrong method.)

How best can one instantiate and populate the vector at input_record[rec_type]? Once populated, the vector won't be modified. Is it as simple as input_record[rec_type] = fields? Do, or can, "move semantics" apply in this situation?

Was it helpful?

Solution

In standard associative containers, what you push in is a std::pair, most of the time using std::make_pair().

#include <vector>
#include <string>
#include <unordered_map>
#include <iostream>

using namespace std;

int main()
{
    unordered_map<string, vector<string>> input_records;
    string rec_type {"name"};
    vector<string> fields { "field 1", "field 2", "field 3" };   

    input_records.insert( make_pair( rec_type, fields ) );

    for( const auto& text : input_records[rec_type] )
        cout << text << '\n';
}

This is true for Boost containers too as they are based on the standard ones.

Also, since C++11 there is another function, emplace(), which allows you to create "in place" a pair instead of having to build it first then pass it to the container:

#include <vector>
#include <string>
#include <unordered_map>
#include <iostream>

using namespace std;

int main()
{
    unordered_map<string, vector<string>> input_records;
    string rec_type {"name"};
    vector<string> fields { "field 1", "field 2", "field 3" };   

    input_records.emplace( rec_type, fields );

    for( const auto& text : input_records[rec_type] )
        cout << text << '\n';
}

Depending on how you pass data (rvalue reference? move? etc.) there will be more or less copies. However, a simple rule of thumb that works with all standard containers is that you should just try to use emplace() until you are in a situation where you have to use insert().

Using input_record[rec_type] works as you would expect. However, I would suggest using find() instead because the [] operator will add a new element if it's not found, whild find() will return an iterator which will be container.end() in case it don't find the element, so you just compare the iterator to end() to know if the element was found or not. This is true for all standard associative containers and boost ones.

Most of the time I use associative containers encapsulated into a class which represent the concept I want: like a InputRecord class. Then I provide a find function (or whatever action name suiting the domain) which does exactly what I want, depending on the case: sometime I want it to throw exception if the object is not found, sometime I want to create a new record when the one I look for isn't there, sometime I want to return a pointer, sometime I prefer to return a boost::optional.

You'd better do the same: encapsulate your concept under a class, expose services that are needed, then use whatever you want, however you want inside the class. You can easily even switch to another container if you need to later, without changing the interface, just by changing the code inside the class that will manipulate the container.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top