efficient way to get key from std::map value - c++

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

Related

How to extract map field from protobuf message for C++ consumer

I have a proto3 file. There is a message in the proto file that contains a map field.
proto3
package abc.xyz.testproto
message abc
{
map <string, int32> mapfield = 1;
}
Suppose as a c++ consumer i want to read this map field, what would be the api call neccessary for me to read these key and value from this map field into a string and int field in c++ ?
I tried to look for it here : https://developers.google.com/protocol-buffers/docs/reference/cpp-generated#map-fields but could not find how the code on how to consume the map fields. Can someone help me with it ?
Thanks
It works almost the same as std::map:
abc c;
(*c.mutable_mapfield())["abc"] = 2; // set value with operator []
for (const auto &ele : c.mapfield()) { // iterate with range for
cout << "key: " << ele.first << ", value: " << ele.second << "\n";
}

Using lower_bound on nested map

I have a map that looks like
map<string , map<int,int>>
the string contains name of a student, the nested map contains ID as key and age as value. When I print the map, it al prints values as it should.
However, I want to find a students with a certain ID and lower. I tried using lower_bound using:
for( auto &x : class ){
auto it = x.second.upper_bound(some_number);
for( ; it != x .second.begin() ; --it){
cout << x.first << " = " << << it -> first << " " <<it -> second << endl;
}
}
This indeed prints right names of students, but their IDs and ages are just zeros or random numbers, what is causing this behavior? It works when I just print it.
I tried to found out about this on cpp map reference but found nothing.
Following code solves your problem:
for( auto &x : Class ){
auto it = x.second.upper_bound(some_number);
while(it!=x.second.begin()){
it=prev(it);
cout<< x.first<< " = "<< it->first<< " "<< it->second<< endl;
}
}
Refer std::map::upper_bound
What above code does is, first it finds the iterator with id strictly greater than some_number. Now because we want to print "students with a certain ID and lower", we print all the id's lower than the return value of upper_bound.
The stopping condition is that if iterator is itself x.second.begin(), that means now we don't have any id's smaller than it.
Plus your data structure is strange, you should have student ID as your primary index.
map<int, pair<string,int> > would be more appropriate data structure. (Assuming unique id's which is mostly the case).
Although, you could do lot better using OOP concepts.
What you see is probably undefined behaviour, std::map::upper_bound returns also end iterator under some conditions and from your code it does not look like you check for this condition. Also you should not use class keyword as variable name for your map, I am preety sure it does not compile. Below is a sample code that should work with no UB and print all IDs less than some number including this ID:
http://coliru.stacked-crooked.com/a/efae1ae4faa3e656
map< string , map<int,int>> classes ={
{ "k1", {{1,1},{2,2},{3,3}} }
};
//int class;
int some_number = 4;
for( auto &x : classes ){
auto it_num_end = x.second.upper_bound(some_number); // some numberis just variable that contains number
for( auto it = x.second.begin(); it != it_num_end ; ++it){
cout << x.first << " = " << it -> first << " " <<it -> second << endl;
}
}

Iterating through two maps in c++

I would like to loop through two maps at the same time, how could I achieve this?
I have two vectors want to print both, can I do two time (auto it : mymap) within one for? Something like:
for (auto it: mymap && auto on: secondMap)
is this even allowed?
I am trying to print values like (value1, value2) where each of the values is in a different map. The maps do not necessarily contain the exact same items but the key is an Instruction and the value is an integer, so if I have a element in the map for value2, then not necessarily there is a value1 corresponding to the same key, but in that case it should be 0 which is the default integer value.
Any ideas?
Perhaps it is possible to combine two iterators, one for each map?
Kind regards,
Guus Leijsten
You can use the regular for-loop for this :
#include <iostream>
#include <map>
int main(int argc, char* argv[]) {
std::map<int, std::string> m1, m2;
m1.insert({15, "lala"});
m1.insert({10, "hey!"});
m1.insert({99, "this"});
m2.insert({50, "foo"});
m2.insert({51, "bar"});
for(auto it_m1 = m1.cbegin(), end_m1 = m1.cend(),
it_m2 = m2.cbegin(), end_m2 = m2.cend();
it_m1 != end_m1 || it_m2 != end_m2;)
{
if(it_m1 != end_m1) {
std::cout << "m1: " << it_m1->first << " " << it_m1->second << " | ";
++it_m1;
}
if(it_m2 != end_m2) {
std::cout << "m2: " << it_m2->first << " " << it_m2->second << std::endl;
++it_m2;
}
}
return EXIT_SUCCESS;
}
Note that because you want to iterate over maps of different size, you have to use the || operator in loop condition. The direct consequence is that you cannot increment in the last part of the for-loop, as one of the iterator may be invalid at that time (and lead to a segmentation fault).
You have to check iterator validity inside the loop and increment it when it's valid, as shown in the sample above.

Implementation of stl map in c++ using class of values

I have to develop stl map in which key is integer where value associated with key is 5 tuple.
having integer data types only.
e.g key=1
value=(2,3,4,5,6)
key=2
value=(1,2,3,4,5)
and so on.
how i can implement it for insert and search operation.
mean how to map single key value to tuple of 5 values.
how to accomplish it?
Depending on what your data means I would go for a different approach.
If your values logically belong together, that is if they only make sense in combination, then I would simply store them in a common data structure and store this data structure in a map. For this purpose a class, struct or container might suit your needs. Again it depends on your context what is the best choice.
If your values exist in isolation and the only connection between them is that they share the same key, then I would use std::multimap.
If you have access to C++11, you can make use of std::tuple (or boost´s tuple), which I believe is the best fit data structure to your case. See the snippet below and see if it fits:
#include<tuple>
#include<map>
#include<iostream>
#include<stdexcept>
typedef std::tuple<int, int, int, int, int > fiveIntTuple;
void insert(std::map<int, fiveIntTuple>& values,
int key, int a, int b, int c, int d, int e){
values[key] = std::make_tuple(a,b,c,d,e);
}
fiveIntTuple search(const std::map<int, fiveIntTuple >& values, int key ){
return values.at(key);
}
void print(const std::map<int, fiveIntTuple >& values, int key){
try{
fiveIntTuple t;
t = search(values, key);
std::cout << "For key == " << key << " got: "
<< std::get<0>(t) << ","
<< std::get<1>(t) << ","
<< std::get<2>(t) << ","
<< std::get<3>(t) << ","
<< std::get<4>(t) << std::endl;
}catch(const std::out_of_range&){
std::cerr << "For key " << key << " ... not found" << std::endl;
}
}
int main(){
std::map<int, fiveIntTuple > my_values;
insert(my_values, 1, 2,3,4,5,6);
insert(my_values, 2, 1,2,3,4,5);
print(my_values, 1);
print(my_values, 2);
print(my_values, 3);
return 0;
}
Executing this snippet you must get:
For key == 1 got: 2,3,4,5,6
For key == 2 got: 1,2,3,4,5
For key 3 ... not found

changing value in a stl map in place

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.