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

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";
}

Related

efficient way to get key from std::map value

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

Accessing element of map of vector of maps (c++)

I've been working on a simple database system managment and I've come up with:
std::map< std::string, std::vector < std::map < std::string,
boost::variant <std::string, size_t, double bool> > > tables;
I have a map (tables) of vectors (table) of maps (records) and I've allready wrote a function to read a file to it but I'm not really sure how to access the single attributes.
I can print the whole thing with:
for(auto table: tables)
for(auto record : table.second)
for(auto attribute : record) {
std::cout << j.second;
I tried doing something like:
std::cout << tables["credentials"][2]["username"];
This however does not work; it only prints a blank line.
It's most likely that you are using wrong keys of the maps to access the database.
Update your code to print the contents of your database so you can see the keys also in the maps.
for(auto table: tables)
{
std::cout << "Key: " << table.first << std::endl;
for(auto record : table.second)
{
for(auto attribute : record)
{
std::cout << "Key: " << attribute.first
<< ", Value: " << attribute.second << std::endl;
}
}
}

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;
}
}

Printing a multi-dimensional map in c++

I'm having trouble printing a multi-dimensional map. I initialize it by
map<string, map<string, vector<double>>> mapData;
void Graph::addToGraph(string start, string next, double cos, double mile)
{
//see if the map contains the starting city string, add if not found
if (mapData.find(start) == mapData.end())
{
mapData[start][start].push_back(invalid);
mapData[start][start].push_back(invalid);
}
//see if the map contains the next city string, add if not found
if (mapData.find(next) == mapData.end())
{
mapData[next][next].push_back(invalid);
mapData[next][next].push_back(invalid);
}
mapData[start][next].push_back(cos);
mapData[start][next].push_back(mile);
double check = mapData[start][next][0];//test case
}
As far as I can tell it seems to me the map has been created correctly. I used a third dimension so I can store the mileage and the price. This will be used in a shortest path algorithm. I want to print each city, as well as all the connecting flights to the city with their mileage and price example, SFA SLC $700 59miles. Is this even possible, and if not how would I change it?
To give a better idea of why I'm using a map, the requirements of the project is that I read in from a file all the information, create a graph from that file, then let the user enter in the abbreviation of the city they want to start. Maps allowed me to specify a spot in an array with a string which was useful for this. But I'm not sure it will work with this algorithm.
{
for (auto kv : mapData)
{
for (auto kvv : kv.second)
{
if (kvv.second != invalid)
{
cout << kv.first << "<-->" << kvv.first;
cout << " costs $" << kvv.second << endl;
}
}
cout << distance(mapData.begin(), mapData.find("SLC"));
}
}
This is the function that I used to print my map. Thanks to Lucas for pointing me in the right direction
One of the easiest ways:
for(auto& kv : map)
{
std::cout << kv.first << std::endl;
std::cout << kv.second.first << std::endl;
for(double d : kv.second.second
{
//..
}
//..
}
If you're doing other things with the data while you're looping over it you can create a vector of keys and push_back() kv.first within the loop, and then create another loop that iterates over the keys. I'm not certain that this is the fastest way but it certainly isn't too slow.
std::vector<std::string> keys;
for(auto& kv : map)
{
keys.push_back(kv.first);
}
for(auto s : keys)
{
for(double d : map[s])
{
//..
}
}

Iterating and using find() in a map c++

I have a map storing a "job" as a key and a "name" as the value it stores.
map<string, string>dutyAndJob;
map<string,string>::iterator it;
I'm basically trying to look through this map for a particular "name".
If the name isn't there, it shouldn't enter this loop, however, for some unknown reason, it always enters this loop:
string name = "Bob";
it = dutyAndJob.find(name);
if (it == dutyAndJob.end())
{
cout << "Testing : " << name << endl;
}
For some reason, it would still enter this loop even though there isn't a Bob stored in the map.
Any help would be greatly appreciated!
if (it == dutyAndJob.end())
{
cout << "Testing : " << name << endl;
}
should be:
if (it != dutyAndJob.end()) // Does it refer to a valid association
{
cout << "Testing : " << name << endl;
}
Notice the change from == to != indicating that the key was found in the map. The iterator it is only equal to dutyAndJob.end() if the key was not found.
Just realized that Job is the key, and Name is the data. The way you have it structured, you can only use find on the Job that will retrieve the Name.
string job = "Cashier";
it = dutyAndJob.find(job);
if (it == dutyAndJob.end())
{
cout << "Testing : " << job<< endl;
}
If you actually wanted to search by Name, maybe the Name should be the key, and the Job should be the data?