I have defined a Map
boost::unordered_map<"std::string,std::string">m_mapABC ;
And I Store values in it Like m_mapABC[strValue1]=strValue2;
And Assume that i store 10 entries to the map.In that case can the same Key Value be used to store 10 different Values..or will it be over written every time...I guess it would.
In that case using std::pair would help i guess.
std::map<"std::string, std::pair<"std::string", bool>>myMap2
std::pair can have 2 Key Values Equal(I guess I am Right)...What will be the bool value in each case,will it be TRUE in the first case and FALSE the second time or vice-versa?.
I also heard about std::tuple or boost::tuple where a single Key can be used to Store Different Values.
I am not very clear about how to iterate through them...i need help
You may want multimap instead of map.
If you want to associate more than one value with a single key, use std::multimap (or std::unordered_multimap) instead of std::map.
In some cases, it can make sense to have a std::map<key_type, std::vector<mapped_type> > instead (personally, I frequently find this preferable).
If you want to store multiple items with the same key, you should use a multimap (also applies to unordered_ variants).
The following should work:
std::multimap<std::string,int> mm;
for( int i = 0; i != 10; ++i )
mm.insert(make_pair("hello world"), i);
And your multimap should contain ten entries with key "hello world" and 10 different values.
Related
In my code I'm storing parameters sets for interactions between different groups in a map. Currently at startup I add each structure (testvals in the code below) with the key created from joining the two group names into a single string.
string nKey = key1;
nKey += JOIN_STRING;
nKey += key2;
map< string, struct> mymap_string;
mymap_string.insert( make_pair(nKey, testval ));
When it comes to looking up the data for two groups, I'm again creating that string and then using find on the map to retrieve my data.
string nKey = key1;
nKey += JOIN_STRING;
nKey += key2;
auto it = mymap_string.find( nKey );
if ( it != mymap_string.end() )
{
struct vals= it->second;
}
In my code I'm creating the map once at startup but doing the lookup part millions of times. I'm wondering if there's a better way of doing this as string concatenation seems to be relatively expensive and find may not be the fastest way to search and compare strings?
My testing seems to show that strings are faster than using std::pair<string1, string2> as the key for the map. I've looked at map vs unordered_map but there doesn't seem to be much of a difference. unordered_map may be slightly faster when the number of keys is large.
Does anyone have any suggestion on what might be a better, quicker approach? Given the number of calls made to this, if I can make it significantly quicker I can save a lot of time. I don't mind if the insertion or setup isn't blindingly fast since it only happens once, but lookup is important. It would be better to use something standard that works on Windows and Linux.
Update:
OK so from the questions it seems that more background information is required.
testvals is a structure of doubles for the input parameters for the current model being used and the number of variables provided in it will vary with the model. But typically this is between 4-10 values. A typical set is show here:
typedef struct
{
double m_temp_min;
double m_temp_max;
double m_liquid_content;
double m_growth_rate;
double m_alpha;
double m_beta;
} testvals;
Key1 and Key2 are always strings that are passed from the programs core module, but the strings are user-defined, meaning they could be anything from "a" to "my_big_yellow_submarine_3".
The number of keys in the map will depend on the number of groups in the data. If there are only two groups for which interactions parameters need to be provided, then the map would only have 4 unique string keys: group1~~group1, group1~~group2, group2~~group1 and group2~~group2. Normally there are 3 or 4 group types in the map so the number of keys is usually in the number of tens. This size may be why I don't see much of a difference in map and unordered_map performance.
One of the comments mentioned std::pair<std::string,std::string> and as I originally said, the cost of calling make_pair() seems much higher than the cost of making the string and was more than 50% slower when I tested it. But I didn't try the combination of std::pair with unordered_map. I assumed that if std::pair is slower with map, it is also going to be slower with unordered_map. Is there a reason to expect it to be very different?
I hope this helps clarify some of the things.
You have only a limited number of keys which makes calculating the hash expensive compared to the real lookup. That's why std::map and std::unordered_map aren't much different in your case. Besides JOIN_STRING also introduces unnecessary operations while computing hash or comparing strings
I suggest you to avoid those group names altogether and use group IDs instead. With N group types you only have N2 different types of interactions. Then the IDs will belong to a range of [0, N). If N is known at compile time you can even make it an array. So instead of
string nKey = key1;
nKey += JOIN_STRING;
nKey += key2;
you'll use
std::vector<testvals> vals(N*N); // vector with N² elements
uint32_t nKey = key1*N + key2; // index of the <key1, key2> mapping
const auto &val = vals[nKey]; // get the mapped value
You should use & to get a reference instead of a copy. You can also use a map instead of a vector. It's still much slower than a vector but still much faster than a map of string. You can calculate the mapped key like above, or use some mappings like nKey = (key1 << 16) ^ key2 or nKey = ((uint64_t)key1 << 32) | key2
Group names are only used when you convert the names to ID at the beginning, or when you want to print them out. You can use some struct like this to store the name
struct GroupInfo
{
std::string groupName;
uint32_t groupID;
}
No need to use typedef for structs in C++ like in your code. You can also use std::vector<std::string> or std::map<uint32_t, std::string> to map from ID to name. The ID can be a smaller type like uint8_t or uint16_t
Lets say I have a complex map defined as
std::map<int, std::pair< vector<int>, float> > complex_map;
Let us assume I initialized this map as
for (int k=0;k<5;k++)
{
std::pair< vector<int>, float> complex_map_child;
complex_map[k]=complex_map_child;
}
Next, I populate some entries of this map:
float test_1 = .7888;
vector<int> test_2;
test_2.push_back(1);
test_2.push_back(2);
complex_map[1].first = test_2;
complex_map[1].second = test_1;
So corresponding to key value 1 of complex_map, I have the pair of values corresponding to test_1 and test_2.
Now how do I check if I have explicitly added values to the map? i.e in this example how do I say that I havent explicitly filled up say complex_map[0]?
Looks like you are using std::map::operator[] improperly and try to go over it - you are getting object like this:
auto &complex_value = complex_map[0];
and now you try to check if you inserted it before or it was implicitly created by std::map::operator[].
Just do not use std::map::operator[] to access elements. Only use it when you need to set value in a map.
So proper solution would be to use different methods:
// I just want to check if key 0 is there
if( complex_map.count( 0 ) ) {
...
}
// I want to access element by key 0 if it is there
auto it = complex_map.find( 0 );
if( it != complex_map.end() ) {
auto &complex_value = it->second;
...
}
and so on. I know that writing complex_map[0] is shorter but you are creating a problem that trying to solve convoluted way.
Now how do I check if I have explicitly added values to the map? i.e in this example how do I say that I havent explicitly filled up say complex_map[0]?
If by "explicitly" you mean you want to find elements that you wrote to after the initialisation loop that executed complex_map[k]=complex_map_child;, then:
you can compare values in the map to complex_map_child to see if they're equal
you can not detect if a map entry was written to with the same value, or changed then reverted (unless you change the data types to ones that track that themselves, or add some extra bookkeeping outside the map yourself)
I wanted to know if there was a way to find an int in a double type map container. For instance in the following example
std::map<double,double> mt;
mt[2.33] =3.45;
if(mt.find(2)!=mt.end()) //How to do a search for an int instead of a map
{
//Found
}
I wanted to know if there was a way to tell the map to search for an int instead of a double. Since the map would search for a double by default.
One way you can do this is to use lower_bound/upper_bound member functions to get a range of values around your integer, and then check this range manually.
Other way is to use a map with custom comparator that compares keys as integers (see std::map referernce), so you preserve initial key values and can search for integers. But you can't search for doubles then.
Anyways, the task is a bit strange, you probably should reconsider your data structures choice for your problem.
The following should work:
it = mt.lower_bound(2);
However, you need to check the item afterwards;
it->first<3;
must yield true for correct result.
if you are interested only in the integral part (or anything else, as you can use a lambda for that), you might use
auto result = find_if(begin(mt), end(mt),
[&](pair<double, double> p){return (int)(p.first) == 2)}
)
if (result != mt.end())
{
// do your stuff
}
The use case for such a kind of approach still remains unclear...
I would like to access/iterate over all non-unique keys in an unordered_multimap.
The hash table basically is a map from a signature <SIG> that does indeed occur more than once in practice to identifiers <ID>. I would like to find those entries in the hash table where occurs once.
Currently I use this approach:
// map <SIG> -> <ID>
typedef unordered_multimap<int, int> HashTable;
HashTable& ht = ...;
for(HashTable::iterator it = ht.begin(); it != ht.end(); ++it)
{
size_t n=0;
std::pair<HashTable::iterator, HashTable::iterator> itpair = ht.equal_range(it->first);
for ( ; itpair.first != itpair.second; ++itpair.first) {
++n;
}
if( n > 1 ){ // access those items again as the previous iterators are not valid anymore
std::pair<HashTable::iterator, HashTable::iterator> itpair = ht.equal_range(it->first);
for ( ; itpair.first != itpair.second; ++itpair.first) {
// do something with those items
}
}
}
This is certainly not efficient as the outer loop iterates over all elements of the hash table (via ht.begin()) and the inner loop tests if the corresponding key is present more than once.
Is there a more efficient or elegant way to do this?
Note: I know that with a unordered_map instead of unordered_multimap I wouldn't have this issue but due to application requirements I must be able to store multiple keys <SIG> pointing to different identifiers <ID>. Also, an unordered_map<SIG, vector<ID> > is not a good choice for me as it uses roughly 150% of memory as I have many unique keys and vector<ID> adds quite a bit of overhead for each item.
Use std::unordered_multimap::count() to determine the number of elements with a specific key. This saves you the first inner loop.
You cannot prevent iterating over the whole HashTable. For that, the HashTable would have to maintain a second index that maps cardinality to keys. This would introduce significant runtime and storage overhead and is only usefull in a small number of cases.
You can hide the outer loop using std::for_each(), but I don't think it's worth it.
I think that you should change your data model to something like:
std::map<int, std::vector<int> > ht;
Then you could easily iterate over map, and check how many items each element contains with size()
But in this situation building a data structure and reading it in linear mode is a little bit more complicated.
How do you make a variable name where you create a variable and then in brackets the variable number? (By the way, I'm just guessing out how the code should be so that you get what I'm trying to say.) For example:
int var[5];
//create a variable var[5], but not var[4], var[3], var[2], etc.
Then, the variable number must be able to be accessed by a variable value:
int number = 5;
int var[number]; //creates a var[5], not a var[4], etc.
int var[2]; //creates a var[2], not a var[1], etc.
cout >>var[number];
number = 2;
cin << var[number];
If I'm way off track with my "example", please suggest something else. I need something similar to this for my game to operate, because I must be able to create an unlimited instance of bullets, but they will also be destroyed at one point.
It looks like you are looking for the functionality provided by std::map which is a container used to map keys to values.
Documentation of std::map
Example use
In the below example we bind the value 123 to the integer key 4, and the value 321 to key 8. We then use a std::map<int,int>::const_iterator to iterate over the key/value pairs in our std::map named m.
#include <map>
...
std::map<int, int> m;
m[4] = 123;
m[8] = 321;
for (std::map<int, int>::const_iterator cit = m.begin (); cit != m.end (); ++cit)
std::cout << cit->first << " -> " << cit->second << std::endl;
output:
4 -> 123
8 -> 321
It looks like you want variable length arrays, which is not something C++ supports. In most cases, the correct solution is to use an std::vector instead, as in
int number = 42; // or whatever
std::vector<int> var(number);
You can use std::vector as you would use an array in most cases, and you gain a lot of bonus functionality.
If I understand what you want correctly (which I'm not certain that I do), you want to be able to create a place to hold objects and use them according to some index number, but to only create the specific objects which go in it on demand. You want do to this either because 1) you don't know how many objects you're going to create or 2) you aren't going to use every index number or 3) both.
If (1) then you should probably just use a vector, which is an array-like structure which grows automatically as you add more things to it. Look up std::vector.
If (2) then you could use an array of pointers and initially set all of the values to null and then use new to create the objects as needed. (Or you could use the solution recommend in part 3.)
If (3) then you want to use some form of map or hash table. These structures will let you find things by number even when not all numbers are in use and will grow as needed. I would highly recommend a hash table, but in C++, there isn't one in the STL, so you have to build your own or find one in a third-party library. For ease, you can use std::map, which is part of the STL. It does basically the same thing, but is slower. Some C++ distributions also include std::hash_map. If it's available, that should be used instead because it will be faster than std::map.