I understand that when we insert values into the STL map, a copy is made and stored.
I have code that essentially does a find on the map and obtains an iterator.
I then intend to use the iterator to change the value in the map.
The results are not what I would expect ie: the value is not changed when accessed from another part of the program. I suspect its because the change I am applying is to a copy of
value.
the relevant code is as follows.
ObjectMappingType::iterator it = objectMapping_.find(symbol);
if (it == objectMapping_.end()) {
throw std::invalid_argument("Unknown symbol: " + symbol);
}
get<3>(it->second) = value;
NOTE: I am actually trying to change a value inside a boost::tuple that is stored as the 'value' part of the map.
Hmm... both methods seem to work fine for me. Here's the entire example that I used:
#include <iostream>
#include <map>
#include <string>
#include <boost/tuple/tuple.hpp>
typedef boost::tuple<int, std::string> value_type;
typedef std::map<int, value_type> map_type;
std::ostream&
operator<<(std::ostream& os, value_type const& v) {
os << " number " << boost::get<0>(v)
<< " string " << boost::get<1>(v);
return os;
}
int
main() {
map_type m;
m[0] = value_type(0, "zero");
m[1] = value_type(0, "one");
m[2] = value_type(0, "two");
std::cout
<< "m[0] " << m[0] << "\n"
<< "m[1] " << m[1] << "\n"
<< "m[2] " << m[2] << "\n"
<< std::endl;
boost::get<0>(m[1]) = 1;
map_type::iterator iter = m.find(2);
boost::get<0>(iter->second) = 2;
std::cout
<< "m[0] " << m[0] << "\n"
<< "m[1] " << m[1] << "\n"
<< "m[2] " << m[2] << "\n"
<< std::endl;
return 0;
}
The output is exactly what I would have expected.
lorien$ g++ -I/opt/include -gdwarf-2 foo.cpp
lorien$ ./a.out
m[0] number 0 string zero
m[1] number 0 string one
m[2] number 0 string two
m[0] number 0 string zero
m[1] number 1 string one
m[2] number 2 string two
lorien$
The operator[] on a map will give a reference to the actual contained element, but it has the nasty side-effect of creating a map entry if none existed before. Since you're already checking the result of find() to see if the key exists, you can use it safely.
get<3>(objectMapping_[symbol]) = value;
Without seeing more of your code I can't be sure of this, but it sounds like you could have a threading issue. Does your program use multiple threads by any chance? Maybe not even explicitly, but perhaps you call a library that does some work in a separate thread? Here is what I would do to start debugging.
Have a check that will re-find the value in the map after you set it, and check that it is the correct new value and throw an exception if it is not.
Reproduce the error by accessing the value from the "other part of the program" and see whether it throws the exception
Step through with a debugger to make sure that the modification is indeed happening BEFORE the access in the other part of the program instead of after.
If there are too many accesses to make it practical to do this by hand, dump a trace to a file. That is, add code to append to a log file every time the map is accessed. Each line should have the time of access to as fine a resolution as your system clock allows, the address of the map (so you know you are modifying the same map), the symbol key, the value, and the new value (if this was a modifying access). This way you can pinpoint exactly what times the map modifications are not showing up in the other part of the program, and whether they are before or after the access.
Related
I have a map as below :
std::map< std::string ,int> mapobj;
mapobj["one"] = 1;
mapobj["two"] = 2;
mapobj["three"] =3 ;
how to get key when input is value
EX :
input : 1
output : one
Note : In my case value is unique
A one-to-one mapping is actually quite easy, the fastest way to do it is to probably maintain two maps, one for each direction. It becomes more complicated if it's not one-to-one since you'll need to provide a way to get a collection of values or key, rather than a single one. Happily, you only have the one-to-one requirement.
One of the maps is the one you have now, the other will map the values to a given key, soboth would be:
std::map<std::string, int> forwardmapobj;
std::map<int, std::string> reversemapobj;
and these would be maintained within a bidimap class of some sort.
Whenever you insert to, or delete from, your bidimap, you have to perform the equivalent operation on both internal maps.
For example, here's some pseudo-code. It maintains the two maps and ensures that they'e kept in sync for whatever operations you have that change the keys and values:
class biDiMap:
map<string, int> forwardMap
map<int, string> reverseMap
void add(string key, int val):
if exists forwardMap[key]: throw exception 'duplicate key'
if exists reverseMap[val]: throw exception 'duplicate value'
forwardMapObj[key] = val
reverseMapObj[val] = key
void delKey(string key):
if not exists forwardMap[key]: throw exception 'no such key'
delete reverseMap[forwardMap[key]]
delete forwardMap[key]
void delVal(int val):
if not exists reverseMap[val]: throw exception 'no such value'
delete forwardMap[reverseMap[val]]
delete reverseMap[val]
int getValFor(string key): return forwardMap[key]
string getKeyFor(int val): return reverseMap[val]
Obviously, there's plenty of other stuff you could add but that should form the basis. In any case, you've probably got enough work ahead of you turning that into a C++ class :-)
If you don't want to roll your own solution, then Boost has a very good one that you can pretty well use as is. Boost.Bimap provides a fully-templated bi-directional map that you should be able to use with minimal code, such as the following complete program:
#include <iostream>
#include <string>
#include <boost/bimap.hpp>
using std::string;
using std::cout;
using std::exception;
using boost::bimap;
int main()
{
typedef bimap<string, int> SiMap;
typedef SiMap::value_type SiEntry;
SiMap bidi;
bidi.insert(SiEntry("ninety-nine", 99));
int i = 0;
for (string str: {"one", "two" , "three", "four", "five", "six"}) {
bidi.insert(SiEntry(str, ++i));
}
cout << "The number of entries is " << bidi.size() << "\n\n";
for (auto i = 1; i <= 7; i += 3) {
try {
cout << "Text for number " << i << " is " << bidi.right.at(i) << "\n";
} catch (exception &e) {
cout << "Got exception looking up number " << i << ": " << e.what() << "\n";
}
}
cout << "\n";
for (auto str: {"five", "ninety-nine", "zero"}) {
try {
cout << "Number for text '" << str << "' is " << bidi.left.at(str) << "\n";
} catch (exception &e) {
cout << "Got exception looking up text '" << str << "': " << e.what() << "\n";
}
}
cout << "\n";
return 0;
}
It creates a bi-directional mapping between the textual form of a number and the integral value, then does a few lookups (in both directions) to show that it works:
The number of entries is 7
Text for number 1 is one
Text for number 4 is four
Got exception looking up number 7: bimap<>: invalid key
Number for text 'five' is 5
Number for text 'ninety-nine' is 99
Got exception looking up text 'zero': bimap<>: invalid key
I do notice that this has the "stdmap" tag, so this may not be appropriate. However Boost has boost::bimap<> which will allow you to do what you want: it allows lookup by either key or value.
how to get key when input is value
First, there is no guarantee that value is unique. I realize that you are saying it is unique. Still, conceptually speaking, this is something to keep in mind when looking at the problem.
Second, std::map is not sorted by value. Hence, the most efficient algorithm to look for a value will be O(N) on an average.
Try boost Bimap. all the things you are trying to do can simply be done by it.
1 --> one
2 --> two
...
one --> 1
two --> 2
...
here is a link where a working example is present.
here
Each time to find an element I use find and thus, I have a pointer returned to it (iterator would be better to describe it rather than pointer). But, in the case the element is not there, I go create it.
The problem is that after both cases I want to have something that will point to that element no matter if it existed or not (because I ensured that I created it). My solution is to use find (for the second time, which I guess is costly), but I think that there could be a unified way to hold a reference to the item without doing a second search (either by previous find or while creating the item).
Is this possible?
This is a common pattern -- you can use insert() which returns an iterator whether something was added or not:
#include <iostream>
#include <unordered_map>
#include <string>
using namespace std;
int main()
{
std::unordered_map<int, string> m;
auto result = m.insert(std::make_pair(0, "Foo"));
if(result.second)
cout << "Inserted: " << result.first->first << " -> " << result.first->second << '\n';
result = m.insert(std::make_pair(0, "Bar"));
if(!result.second)
result.first->second = "Bar";
for(auto i : m)
cout << i.first << " -> " << i.second << '\n';
return 0;
}
Output:
Inserted: 0 -> Foo
0 -> Bar
Use std::unordered_map::insert.
It's return value is std::pair<iterator,bool>, where boolean value indicates whether actual insertion took place or the value already existed. See documentation for return value:
1-2) Returns a pair consisting of an iterator to the inserted element
(or to the element that prevented the insertion) and a bool denoting
whether the insertion took place.
I've been reading the cplusplus.com site and trying to make sure that my unordered_set of numbers won't be modified in any way. The site says that the elements of a container are not sorted which is the case with plain set.
The site also says this:
Internally, the elements in the unordered_set are not sorted in any
particular order, but organized into buckets depending on their hash
values to allow for fast access to individual elements directly by
their values.
I have no clue what that means really (can you explain btw.?). Consider following example:
typedef const std::unordered_set<short> set_t;
set_t some_set = {1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36};
Can I make sure that the above set "some_set" will never be changed and that the numbers will always stay in the same order (because this is the goal here)?
I'm also not planning to insert or remove numbers from the set.
typedef const std::unordered_set<short> set_t;
set_t some_set = {1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36};
The changing order of the numbers in some_set depends on the operations that you do to some_set. The contents of some_set immediately after creation is not defined, but it probably won't be {1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36}. You can see this with a simple demo:
#include <iostream>
#include <unordered_set>
int main() {
typedef const std::unordered_set<short> set_t;
set_t some_set = {1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36};
for (short s : some_set)
std::cout << s << std::endl;
// The order won't change if we don't modify the contents
std::cout << "AGAIN!" << std::endl;
for (short s : some_set)
std::cout << s << std::endl;
// If we put a bunch of stuff in
for (short s = 31; s < 100; s += 4)
some_set.insert(s);
// The elements from *before* the modification are not necessarily in the
// same order as before.
std::cout << "MODIFIED" << std::endl;
for (short s : some_set)
std::cout << s << std::endl;
}
I am confused which is more efficient?
As we can access map directly, why do we need to use find?
I just need to know which way is more efficient.
#include <iostream>
#include <map>
using namespace std;
int main ()
{
map<char,int> mymap;
map<char,int>::iterator it;
mymap['a']=50;
mymap['b']=100;
mymap['c']=150;
mymap['d']=200;
//one way
it=mymap.find('b');
cout << (*it).second <<endl;
//another way
cout << mymap['b'] <<endl;
return 0;
}
thanks in advance! :)
Using find means that you don't inadvertently create a new element in the map if the key doesn't exist, and -- more importantly -- this means that you can use find to look up an element if all you have is a constant reference to the map.
That of course means that you should check the return value of find. Typically it goes like this:
void somewhere(const std::map<K, T> & mymap, K const & key)
{
auto it = mymap.find(key);
if (it == mymap.end()) { /* not found! */ }
else { do_something_with(it->second); }
}
As we can access map directly, why do we need to use find?
Because map<>::operator[] is sometimes nasty. If an element doesn't exist then:
it inserts it
value initialize it
returns reference of value
Thus it always returns a valid reference of value, even if a key din't exist previously. This behavior is not intended many times.
On the other hand map<>::find() is safer; because it returns end(), if a value doesn't exit. Another advantage of find() is that it returns an iterator which contains references to key (first) and value(second) both.
The [] operator in map is not constant it is logarithmic. Most of the books stress on this fact and point out it is a bit misleading. So both find and [] operator are with the same complexity.
Please note that the [] operator will create the entry even if it does not exist while find will return end() in that case.
This code and doc is picked from cplusplus.com
// accessing mapped values
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main ()
{
map<char,string> mymap;
mymap['a']="an element";
mymap['b']="another element";
mymap['c']=mymap['b'];
cout << "mymap['a'] is " << mymap['a'] << endl;
cout << "mymap['b'] is " << mymap['b'] << endl;
cout << "mymap['c'] is " << mymap['c'] << endl;
cout << "mymap['d'] is " << mymap['d'] << endl;
cout << "mymap now contains " << (int) mymap.size() << " elements." << endl;
return 0;
}
OP:
mymap['a'] is an element
mymap['b'] is another element
mymap['c'] is another element
mymap['d'] is
mymap now contains 4 elements.
Notice how the last access (to element 'd') inserts a new element in the map with that key and initialized to its default value (an empty string) even though it is accessed only to retrieve its value. Member function map::find does not produce this effect.
I want to check if a certain key is found in a map, if so i want to put it in a variable for other uses, but the thing is I dont want to use iterators. I found the find function in the map class, but it returns an iterator, I want to like check if a key is found in map, if it returns true to obtain it
Thanks
std::map::count() will inform you if the map contains a particular key. If the key is in the map, then you could use operator[] to get at the value, knowing that a default value will not be added (though as ildjarn points out would require two searches of the map):
std::map<int, int> m;
m[0] = 1;
m[1] = 2;
if (m.count(0))
{
std::cout << "value=" << m[0] << "\n";
}
Why find() is unattractive, is not clear to me:
std::map<int, int>::iterator i = m.find(0);
if (i != m.end())
{
std::cout << "value=" << i->second << "\n";
}