boost property tree getting first element - c++

I was wondering if there where some convenient way to access a known index of a list using the path methodology.
My dream method
float v = pt.get<float>("root.list[0]);
Current known method (or something like it)
ptree::value_type listElement;
BOOST_FOREACH(listElement,tree.get_child("root.list")){
return listElement.second.get<float>();
}
Format of List (json)
{
root:{
list:[1,2,3,4,5]
}
}

You should be able to access the range of elements in the list using boost::property_tree::equal_range. With the JSON you format you are using there is no name element associated with each item in the list. This means that it is necessary to get the parent node prior to accessing the child elements in the range.
The code below is a crude example that you could adapt:
Input Json File (in.json) :
{
"root" :
{
"list" : [1,2,3,4,5]
}
}
Function to print the nth element of the list:
void display_list_elem( const ptree& pt, unsigned idx )
{
// note: the node elements have no name value, ergo we cannot get
// them directly, therefor we must access the parent node,
// and then get the children separately
// access the list node
BOOST_AUTO( listNode, pt.get_child("root.list") );
// get the children, i.e. the list elements
std::pair< ptree::const_assoc_iterator,
ptree::const_assoc_iterator > bounds = listNode.equal_range( "" );
std::cout << "Size of list : " << std::distance( bounds.first, bounds.second ) << "\n";
if ( idx > std::distance( bounds.first, bounds.second ) )
{
std::cerr << "ERROR Index too big\n";
return;
}
else
{
std::advance( bounds.first, idx );
std::cout << "Value # idx[" << idx << "] = "
<< bounds.first->second.get_value<std::string>() << "\n";
}
std::cout << "Displaying bounds....\n";
display_ptree( bounds.first->second, 10 );
}

Related

Query nested BSON documents with mongo c++ driver

I have a bsoncxx::document::view bsonObjViewand a std::vector<std::string> path that represents keys to the value we are searching for in the BSON document (first key is top level, second key is depth 1, third key depth 2, etc).
I'm trying to write a function that given a path will search the bson document:
bsoncxx::document::element deepFieldAccess(bsoncxx::document::view bsonObj, const std::vector<std::string>& path) {
assert (!path.empty());
// for each key, find the corresponding value at the current depth, next keys will search the value (document) we found at the current depth
for (auto const& currKey: path) {
// get value for currKey
bsonObj = bsonObj.find(currKey);
}
// for every key in the path we found a value at the appropriate level, we return the final value we found with the final key
return bsonObj;
}
How to make the function work? What type should bsonObjbe to allow for such searches within a loop? Also, how to check if a value for currKey has been found?
Also, is there some bsoncxx built in way to do this?
Here is an example json document followed by some paths that point to values inside of it. The final solution should return the corresponding value when given the path:
{
"shopper": {
"Id": "4973860941232342",
"Context": {
"CollapseOrderItems": false,
"IsTest": false
}
},
"SelfIdentifiersData": {
"SelfIdentifierData": [
{
"SelfIdentifierType": {
"SelfIdentifierType": "111"
}
},
{
"SelfIdentifierType": {
"SelfIdentifierType": "2222"
}
}
]
}
}
Example paths:
The path [ shopper -> Id -> targetValue ] points to the string "4973860941232342".
The path [ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> targetValue ] points to the object { "SelfIdentifierType": { "SelfIdentifierType": "111" } }.
The path [ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> SelfIdentifierType -> targetValue ] points to the object { "SelfIdentifierType": "111" }.
The path [ SelfIdentifiersData -> SelfIdentifierData -> array_idx: 0 -> SelfIdentifierType -> SelfIdentifierType -> targetValue ] points to the string "111".
Note that the paths are of the type std::vector<std::string> path. So the final solution should return the value that the path points to. It should work for arbitrary depths, and also for paths that point TO array elements (second example path) and THROUGH array elements (last 2 example paths). We assume that the key for an array element at index i is "i".
Update: Currently, the approach suggested by #acm fails for paths with array indices (paths without array indices work fine). Here is all the code to reproduce the issue:
#include <iostream>
#include <bsoncxx/json.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/instance.hpp>
std::string turnQueryResultIntoString3(bsoncxx::document::element queryResult) {
// check if no result for this query was found
if (!queryResult) {
return "[NO QUERY RESULT]";
}
// hax
bsoncxx::builder::basic::document basic_builder{};
basic_builder.append(bsoncxx::builder::basic::kvp("Your Query Result is the following value ", queryResult.get_value()));
std::string rawResult = bsoncxx::to_json(basic_builder.view());
std::string frontPartRemoved = rawResult.substr(rawResult.find(":") + 2);
std::string backPartRemoved = frontPartRemoved.substr(0, frontPartRemoved.size() - 2);
return backPartRemoved;
}
// TODO this currently fails for paths with array indices
bsoncxx::document::element deepFieldAccess3(bsoncxx::document::view bsonObj, const std::vector<std::string>& path) {
if (path.empty())
return {};
auto keysIter = path.begin();
const auto keysEnd = path.end();
std::string currKey = *keysIter; // for debug purposes
std::cout << "Current key: " << currKey;
auto currElement = bsonObj[*(keysIter++)];
std::string currElementAsString = turnQueryResultIntoString3(currElement); // for debug purposes
std::cout << " Query result for this key: " << currElementAsString << std::endl;
while (currElement && (keysIter != keysEnd)) {
currKey = *keysIter;
std::cout << "Current key: " << currKey;
currElement = currElement[*(keysIter++)];
currElementAsString = turnQueryResultIntoString3(currElement);
std::cout << " Query result for this key: " << currElementAsString << std::endl;
}
return currElement;
}
// execute this function to see that queries with array indices fail
void reproduceIssue() {
std::string testJson = "{\n"
" \"shopper\": {\n"
" \"Id\": \"4973860941232342\",\n"
" \"Context\": {\n"
" \"CollapseOrderItems\": false,\n"
" \"IsTest\": false\n"
" }\n"
" },\n"
" \"SelfIdentifiersData\": {\n"
" \"SelfIdentifierData\": [\n"
" {\n"
" \"SelfIdentifierType\": {\n"
" \"SelfIdentifierType\": \"111\"\n"
" }\n"
" },\n"
" {\n"
" \"SelfIdentifierType\": {\n"
" \"SelfIdentifierType\": \"2222\"\n"
" }\n"
" }\n"
" ]\n"
" }\n"
"}";
// create bson object
bsoncxx::document::value bsonObj = bsoncxx::from_json(testJson);
bsoncxx::document::view bsonObjView = bsonObj.view();
// example query which contains an array index, this fails. Expected query result is "111"
std::vector<std::string> currQuery = {"SelfIdentifiersData", "SelfIdentifierData", "0", "SelfIdentifierType", "SelfIdentifierType"};
// an example query without array indices, this works. Expected query result is "false"
//std::vector<std::string> currQuery = {"shopper", "Context", "CollapseOrderItems"};
bsoncxx::document::element queryResult = deepFieldAccess3(bsonObjView, currQuery);
std::cout << "\n\nGiven query and its result: [ ";
for (auto i: currQuery)
std::cout << i << ' ';
std::cout << "] -> " << turnQueryResultIntoString3(queryResult) << std::endl;
}
There is not a built-in way to to do this, so you will need to write a helper function like the one you outline above.
I believe the issue you are encountering is that the argument to the function is a bsoncxx::document::view, but the return value of view::find is a bsoncxx::document::element. So you need to account for the change of type somewhere in the loop.
I think I would write the function this way:
bsoncxx::document::element deepFieldAccess(bsoncxx::document::view bsonObj, const std::vector<std::string>& path) {
if (path.empty())
return {};
auto keysIter = path.begin();
const auto keysEnd = path.end();
auto currElement = bsonObj[*(keysIter++)];
while (currElement && (keysIter != keysEnd))
currElement = currElement[*(keysIter++)];
return currElement;
}
Note that this will return an invalid bsoncxx::document::element if any part of the path is not found, or if the path attempts to traverse into an object that is not a actually a BSON document or BSON array.

Range of the first element of a vector pair

I need to find the range of the first elements of a vector pair. I need this range for a map, which counts the duplicate entries in this vector.
Here is a code snipped and how I managed it. Maybe there is another, better solution?
unordered_map<int, int> frequency;
vector<pair<unsigned int,Point>> Roi_Num_Koord;
vector<int> Roi_first_Element;
int main()
{
// Part1: fill the Vector pair
Roi_Num_Koord.emplace_back(make_pair(0,Point(3.6));
Roi_Num_Koord.emplace_back(make_pair(1,Point(4,8));
Roi_Num_Koord.emplace_back(make_pair(2,Point(8.3));
Roi_Num_Koord.emplace_back(make_pair(3,Point(4,6));
// Part 2: now copy the first element to another vector
for (int i = 0; i < Roi_Num_Koord.size(); i++)
{
Roi_first_Element.emplace_back(Roi_Num_Koord[i].first);
}
// Part 3: now do the duplicate search (Code was taken out of the internet)
for (int i : Roi_first_Element)
{
++frequency[i];
cout << "freque "<<frequency[i] << endl;
}
for (const auto& e : frequency)
{
if (e.second == 5)
{
std::cout << "Roi " << e.first << " encountered " << e.second << " times\n";
}
}
}
So is there a possibility to remove Part 2 and find out the range of the first Element of Roi_Num_Koord?, so that I don't have to copy the first elements of this vector to the other vector (Roi_first_Element)
Yes the second step is completely redundant. You just iterate through the container and whenever you need first element of the pair you say it explicitly pretty much like you do in Step 2.
for(const pair<unsigned int,Point>& element : Roi_Num_Koord)
{
++frequency[element.first];
cout << "freque " << frequency[element.first] << 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;
}
}

How to insert key and value to map that is inside another map in cpp?

I have this map :
std::map<std::string, std::map<std::string, std::string> > objects;
As far as I know, when I write objects[key] it will replace the value of the key already exists, but here I want it to add to the second map if the pair doesn't exist.
Example:
Current MAP :("key1",map(<"key_a","value_a">,<"key_c","value_d">))
input:
"key1", "key_e","value_f"
relsult:
("key1",map(<"key_a","value_a">,<"key_c","value_d">,<"key_e","value_f">))
input :
"key1", "key_e","value_g"
(replace the value from "value_f" to "value_g")
result:
("key1",map(<"key_a","value_a">,<"key_c","value_d">,<"key_e","value_g">))
and if I get "key2" -> it will insert new key with empty map
can I do this :
objects[key].insert(std::make_pair(key2,value2))
it didn't work for some reason
can you please advise?
As far as I know, when I write objects[key] it will replace the value of the key already exists, but here I want it to add to the second map if the pair doesn't exist.
That's wrong: if you write objects[key] is doesn't replace anything, ever; rather:
If objects doesn't already contain key, it inserts a new pair in the map with that key and for the value it uses a default constructed (in this case empty) std::map<std::string, std::string>. It then returns a reference to the value.
If objects does contain key, it returns a reference to the existing value (the "inner" map) without changing or replacing anything.
can I do this: objects[key].insert(std::make_pair(key2,value2))
You could, but if [key][key2] already existing that wouldn't update the value to be value2, which I assume is what you want and why you say "it didn't work".
All you have to do is:
objects[key][key2] = value2;
Because objects[key] never replaces an existing map, but default-constructs one if key's missing, then key2 either finds the existing entry in the "inner" map that needs updating or creates it, this all hangs together (if I've understood your requirement correctly).
maybe you mean something like this?
http://www.cplusplus.com/reference/map/map/operator%5B%5D/
http://www.cplusplus.com/reference/map/map/count/
std::map<std::string, std::map<std::string, std::string> > objects;
if (objects.count("your_key") > 0)
{ // insert a key ("another_key") into map
objects["your_key"]["another_key"] = "some value";
} else
{ // create the new map for the key
objects["your_key"] = std::map<std::string, std::string>();
}
You can use method insert. Here is a demonstrative program
#include <iostream>
#include <string>
#include <map>
int main()
{
std::map<std::string, std::map<std::string, std::string>> objects;
objects["key1"] = { { "key_a", "value_a" }, { "key_c", "value_d" } };
objects["key1"].insert( { "key_e", "value_f" } );
for ( const auto &p1 : objects )
{
std::cout << "\"" << p1.first << "\": ";
for ( const auto &p2 : p1.second )
{
std::cout << "{ \"" << p2.first
<< "\", \"" << p2.second
<< "\" }" << ' ';
}
std::cout << std::endl;
}
objects["key1"]["key_e"] = "value_g";
for ( const auto &p1 : objects )
{
std::cout << "\"" << p1.first << "\": ";
for ( const auto &p2 : p1.second )
{
std::cout << "{ \"" << p2.first
<< "\", \"" << p2.second
<< "\" }" << ' ';
}
std::cout << std::endl;
}
return 0;
}
The program output is
"key1": { "key_a", "value_a" } { "key_c", "value_d" } { "key_e", "value_f" }
"key1": { "key_a", "value_a" } { "key_c", "value_d" } { "key_e", "value_g" }

c++ map finding value and associated key

I develop one program in c++ in which i have to find key in stl map by using values.
But values assigned to key is the 5 tuples (srcip,port,destip,port,srcno)
Now i want to check in map whether there is key assosiated with values.
I am trying something like this.
But its showing error like
wrong number of template argument.
Note(In my program in pair key->Value) value consist of tuple of 5 variable.
template<class T>
struct map_data_compare : public std::binary_function<typename T::value_type,typename T::mapped_type,bool>
{
public:
bool operator() (typename T::value_type &pair,typename T::mapped_type i)
{
return pair.second == i;
}
}
class Values
{
private:
std::string C_addr;
int C_port;
std::string S_addr;
int S_port;
int C_ID;
public:
Values(std::string,int,std::string,int,int);
void printValues();
};
Values :: Values(std::string Caddr,int Cport,std::string Saddr,int Sport,int Cid)
{
C_addr=Caddr;
C_port=Cport;
S_addr=Saddr;
S_port=Sport;
C_ID=Cid;
}
void Values::printValues()
{
cout << C_addr<<":" <<C_port<<":" << S_addr <<":" <<S_port << ":"<<C_ID <<endl;
}
//In main
{
typedef std::map<int, Values> itemsType;
itemsType items;
Values connection (inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port),inet_ntoa(servaddr.sin_addr),ntohs(servaddr.sin_port),clientID);
std::map<std::int,Values>::iterator it = std::find_if( items.begin(), items.end(), std::bind2nd(map_data_compare<itemsType>(),connection));
if ( it != items.end() )
{
assert( connection == it->second);
std::cout << "Found index:" << it->first << " for values:" << it->second << std::endl;
}
else
{
std::cout << "Did not find index for values:" << connection <<endl;
}
I develop one program in c++ in which i have to find key in stl map by using values.
That's not what maps are meant for. If you need that kind of access, I recommend Boost.Bimap
If the 'key' must be unique, maybe you can try combine the key and value into a std::pair and push them into std::set.
Otherwise you should set your key as value and value as key since you seems mainly use your original value as what we treat to a "key". Then you could use the built-in map::find() function