I am trying to setup a map of map structure in C++ but can't make it work as expected. I put together this sample program to illustrate the issue. Please excuse the mess if it seems convoluted but I want to preserve the case as much as I can.
So the current print out is: L1, size = 0
and what I was expecting is something like:
L1, size 1
L2, 4
It seems like the second level map is not established properly, maybe a scoping issue, but I can't quite figure it out. The program is as the following:
// So the map is
// AKEY -> { BKEY -> [ SegPair, SegPair .... ] }
#include <map>
#include <utility>
#include <iostream>
#include <vector>
typedef std::string AKEY;
typedef std::string BKEY;
typedef std::pair<int,int> SegPair;
typedef std::vector<SegPair> SegVec;
typedef std::map<BKEY, SegVec> Ensemble;
typedef std::map<AKEY, Ensemble> Oracle;
using std::string;
Oracle o = Oracle();
void setup(string akey, string bkey, int x, int y) {
auto pos = o.find(akey);
if (pos == o.end()) {
o[akey] = Ensemble();
}
Ensemble e = o[akey];
auto pos2 = e.find(bkey);
if (pos2 == e.end()) {
e[bkey] = SegVec();
}
SegPair p(x, y);
e[bkey].push_back(p);
}
int main(void) {
setup("L1", "L2", 3, 4);
for (auto it = o.begin(); it != o.end(); it++) {
std::cout << it->first;
Ensemble e = it->second;
std::cout << ", size = " << e.size() << "\n";
for (auto it2 = e.begin(); it2 != e.end(); it2++) {
std::cout << "\t" << it2-> first << "\n";
SegVec v = it2->second;
for (int i = 0; i < v.size(); i++)
std::cout<< v[i].second << " ";
}
}
}
I think your problem is with this line:
Ensemble e = o[akey];
You're creating a local, rather than capturing the lvalue in the map by reference, for mutation. Thus, any changes you make to e after that point will simply be discarded when e goes out of scope.
In setup, e is a copy of the object in o.
When you modify it, you're not modifying anything in o.
To fix, use a reference:
Ensemble& e = o[akey];
That will make e refer to the same thing as o[akey] instead of a copy.
Related
I'm trying to learn how I can use the different typedef vector, and after creating them how I can access the variables. I have commented as I wrote the code
#include <iostream>
#include <vector>
//#include <stdlib.h>
using namespace std;
//Here I wanted to created a structure where I have position vector
struct Point {
double pos;
};
// now I have the type vector definitons named List
typedef std::vector<Point> List;
int main()
{
//I have initialized two list which are List A, and List B
List A;
List B;
//Point a, and Point b are two double data type variables that I want to push back into the two kind of list
Point a;
Point b;
for(int i =0; i<100; i ++){
a.pos = 2*i+1;
b.pos = 2*i -1;
A.push_back(a);
B.push_back(b);
}
return 0;
}
Now my question is how I can access the variables from the two List that I have created. I want to have access to the same index vector.
This is how I tried with dereferencing the pointer, but I think I'm making some mistakes:
for (List::iterator it = A->begin(); it != A->end(); it++) {
Point* val = &(*it);
cout << val->pos()<<endl;
//I need to access the value from B list too
}
Here is the code that would work (assuming A and B are the same length):
size_t jj = 0;
for (List::iterator it = A.begin(); it != A.end(); it++) {
cout << it->pos<<endl;
cout << (B.begin() + jj)->pos<< endl;
jj++;
}
Note that A and B are not pointers so you access their methods normally. By the way, if you have access to C++11 and above, you can use the following:
size_t ii = 0;
for (const auto& el: A) {
cout << el.pos << "," << B.at(ii).pos << endl;
ii++;
}
This has nothing to do with vectors, typedefs or anything else.
val->pos is a double, not a function.
So, remove the ():
Point* val = &(*it);
cout << val->pos << endl;
Or, a little more neatly:
const Point& val = *it;
std::cout << val.pos << '\n';
i'm new to C++. My program is a quiz game which user can choose category and level for the questions. At first, i use the struct data type
struct QuestionInfo
{
string category;
string level;
string question;
string answer;
};
then
vector<QuestionInfo> vec;
The idea of this part is to store the info of the question include (category, level, question and answer) to each element.
Then after building menu and the output questions UI, i go to the filters
void category_filter()
{
for (unsigned int i = 0; i < vec.size(); i ++)
{
if (category_choice != vec[i].category)
vec.erase(vec.begin() + i );
}
}
Void level_filter()
{
for (unsigned int i = 0; i < vec.size(); i ++)
{
if (level_choice != vec[i].level)
vec.erase(vec.begin() + i );
}
}
So the idea of the filters is to delete the elements which not contain the matched category and level. But the output questions did not match with the category and the level i had choose before. I'm not sure what I'm doing wrong.
Let me explain you the problem with my example. Suppose you have a vector of 10 elements, valid indexes are 0 till 9 elements. You have to erase 5th element i == 4. You erase it, then 6th element with index 5 moves to place of 5th elements with index 4. After that you increase i in for, it becomes 5. Thus you skip previous 6th element, that is now 5th with index 4.
You may fix your code like below, moving i ++ to the condition.
for (unsigned int i = 0; i < vec.size(); ) {
if (category_choice != vec[i].category)
vec.erase(vec.begin() + i );
else
i ++;
}
The preferable solution in C++ way is demonstrated by #Jonathan.
You're getting tripped up by not accounting for the indexing shift that occurs when you erase an element. I personally would rely on remove_if and erase with a lambda to accomplish this:
vec.erase(remove_if(begin(vec), end(vec), [&](const auto& i) { return category_choice != i.category; }, end(vec));
vec.erase(remove_if(begin(vec), end(vec), [&](const auto& i) { return level_choice != i.level; }, end(vec));
Alternatively you might consider combining them for a bit of speed improvement:
vec.erase(remove_if(begin(vec), end(vec), [&](const auto& i) { return category_choice != i.category || level_choice != i.level; }, end(vec));
You might want to remove_if + erase:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
int main()
{
struct QuestionInfo
{
std::string category;
std::string level;
std::string question;
std::string answer;
QuestionInfo(std::string category, std::string level, std::string question, std::string answer) :
category(category), level(level), question(question), answer(answer) {}
};
std::vector<QuestionInfo> vec;
std::string category_choice = "cat1";
std::string level_choice = "lev1";
vec.push_back(QuestionInfo("cat1", "lev1", "q1", "a1"));
vec.push_back(QuestionInfo("cat1", "lev2", "q2", "a2"));
vec.push_back(QuestionInfo("cat2", "lev1", "q3", "a3"));
vec.push_back(QuestionInfo("cat2", "lev2", "q4", "a4"));
std::cout << "\nNot filered" << std::endl;
for (auto const &info : vec)
std::cout << "Category:" << info.category << " Level:" << info.level << std::endl;
auto filter_category = std::remove_if(vec.begin(), vec.end(), [&](auto const &info) {return category_choice != info.category; });
vec.erase(filter_category, vec.end());
std::cout << "\nFilered by category" << std::endl;
for (auto const &info : vec)
std::cout << "Category:" << info.category << " Level:" << info.level << std::endl;
auto filter_level = std::remove_if(vec.begin(), vec.end(), [&](auto const &info) {return level_choice != info.level; });
vec.erase(filter_level, vec.end());
std::cout << "\nFiltered by level" << std::endl;
for (auto const &info : vec)
std::cout << "Category:" << info.category << " Level:" << info.level << std::endl;
system("pause");
return 0;
}
As mentioned by others, the remove_if + erase is a standard and expressive way to achieve what you want. But you may also consider non-destructive filtering with a copy_if into a new container, or even without using any additional storage with Boost.Range adaptor boost::adaptors::filtered or boost::filter_iterator. Look here for examples.
I have the following typedefs
typedef map<string, IPAddressPolicyRulesInfo> SecondMap;
typedef map<string, SecondMap> FirstMap;
FirstMap CPCRF::m_mIMSI2PCRFInfo;
Within the c++ function:
within the function, i want to clear the 2nd map. Which is the best way to do this ? Any comments or suggestions are appreciated.
thanks
pdk
Hopefully this might get you started in the right direction:
#include <map>
#include <iostream>
// In general you shouldn't use _t after your own types (some people don't like it)
// But for a post here it doesn't matter too much.
typedef std::map<int, double> map1_t;
typedef std::map<int, map1_t> map2_t;
typedef map2_t::iterator it_t;
int main(int argc, char** argv)
{
// Create the map
map2_t m;
// Add some entries
{ map1_t h; h[0] = 1.1; h[1] = 2.2; m[5] = h; }
{ map1_t h; h[0] = 5.2; h[8] = 7.2; m[1] = h; }
// Output some information
std::cout << m.size() << std::endl;
std::cout << m[5].size() << std::endl;
// For each element in the outer map m
for (it_t it = m.begin(); it != m.end(); ++it)
{
// Assign a friendly name to the inner map
map1_t& inner = it->second;
// Clear the inner map
inner.clear();
}
// Output some information (to show we have done something)
std::cout << m.size() << std::endl;
std::cout << m[5].size() << std::endl;
return 0;
}
I need to get the smallest element in a std::map. I'm aware that there is plenty of documentation available; however, I can't seem to get any to work.
I have two maps, bid and ask, both of which are properties of the Book class. Each is a map of queues. Each of these queues hold Order objects (which have various properties like price, volume, etc.). I have a member function update which obtains the best bid, best ask, and the spread:
void update(void)
{
unsigned long long highest_bid, lowest_ask = 0;
for (std::map<unsigned long long, queue<Order>>::iterator it = this->bid.begin(); it != this->bid.end(); ++it)
{
highest_bid = it->first;
}
// best ask code here
this->bestBid = highest_bid;
this->bestAsk = lowest_ask;
this->spread = labs(this->bestAsk - this->bestBid);
}
Where the ask code is, I've tried the following:
lowest_ask = this->ask.begin()->first;
This compiles, but when I debug it throws an assertion failure (which I've read up on other questions here and can't seem to understand):
Expression: map/set iterator not dereferencable
I've tried reverse iteration:
for(std::map<unsigned long long, queue<Order>>::reverse_iterator rit = this->ask.rbegin(); rit != this->ask.rend(); ++rit)
{
lowest_ask = rit->first;
}
Which compiles and debugs fine, but lowest_ask is always 0, which is wrong. When I step through it in the debugger it doesn't stop until it reaches zero.
I've tried swapping the iterators around:
for(std::map<unsigned long long, queue<Order>>::reverse_iterator rit = this->ask.rend(); rit != this->ask.rbegin(); ++rit)
{
lowest_ask = rit->first;
}
This compiled fine, but once again threw the debug assertion failure.
I could continue on and on on what I've tried, but this question is already over-complicated. I just don't understand why I can't just do what I did at the start (lowest_ask = this->ask.begin()->first).
Thank you very much in advance.
Iterating through the map and always assigning the same variable seems like needlessly hard work.
If you need to access the first item in the map (or the last item in the map) then begin() (or rbegin()) is all you need.
std::map <int, int> themap;
themap[4] = 1;
themap[2] = 2;
themap[1] = 3;
themap[6] = 4;
themap[5] = 5;
themap[7] = 6;
if (!themap.empty())
{
std::cout << "item[" << themap.begin()->first << "] = " << themap.begin()->second << std::endl;
std::cout << "item[" << themap.rbegin()->first << "] = " << themap.rbegin()->second << std::endl;
}
the only time you need to be careful with begin and rbegin is when your map is empty
I think you may just need to check that your containers are not empty so that begin() and rbegin() return something meaningful (defined).
Try this:
void update(void)
{
if(bid.empty() || ask.empty())
return;
// best ask code here
this->bestBid = bid.rbegin()->first;
this->bestAsk = ask.begin()->first;
this->spread = labs(this->bestAsk - this->bestBid);
}
This is not "complicated"; it simply takes some standard debugging measures
#include <map>
#include <iostream>
#include <algorithm>
#include <random>
#include <string>
#include <queue>
namespace mock {
using Order = std::string;
struct Book {
using key_type = unsigned long long;
using order_queue_type = std::queue<Order>;
using property_type = std::map<key_type, order_queue_type>;
property_type bids, asks;
void diagnose(const property_type& prop) {
for (auto it = prop.cbegin(); it != prop.cend(); ++it) {
std::clog << "\t" << it->first << '\n';
}
}
void diagnose() {
std::clog << "bids:" << '\n';
diagnose(bids);
std::clog << "asks:" << '\n';
diagnose(asks);
}
Book() {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<key_type> ba_dist(0, 1000);
std::uniform_int_distribution<std::size_t> len_dist(0, 10);
auto prop_gen = [&] (property_type& prop) {
auto count = len_dist(gen);
for (std::size_t i = 0; i < count; ++i) {
auto val = ba_dist(gen);
auto pair = prop.emplace(val, order_queue_type());
if (!pair.second) {
std::clog << val << " already present" << '\n';
}
}
};
prop_gen(bids);
prop_gen(asks);
}
};
}
int main() {
mock::Book book;
book.diagnose();
}
Instead of the generator in my Book ctor, use your init routines, of course, and your Order type.
SO,
I am looking to print out the contents of a vector; I have tried an iterator for it but that is no good
for(vector<char**>::const_iterator i=myVec.begin();i!=myVec.end();i++) {
cout<<**i<<endl;
}
this does not work, what I am thinking is I will need two iterators (the above one will be the outer one, and the inner one would be as such:
for(vector<char*>::const_iterator j=???;j!=??;j++) {....}
but I haven't been able to get it to work.
Thanks.
Seems to work just fine here:
#include <iostream>
#include <vector>
int main()
{
const char* sentence1[] = {"foo", "bar", "baz"};
const char* sentence2[] = {"xyzzy", "frob", "plugh"};
std::vector<const char**> vec = {sentence1, sentence2};
for (auto i : vec) {
for (size_t w = 0; w < 3; ++w) {
std::cout << i[w] << ' ';
}
}
std::cout << '\n';
}
This will print:
foo bar baz xyzzy frob plugh
The above is C++11. If you don't have that, you'll need to change the vector initialization and the for loop:
std::vector<const char**> vec;
vec.push_back(sentence1);
vec.push_back(sentence2);
for (std::vector<const char**>::iterator it = vec.begin();
it != vec.end(); ++it)
{
for (size_t w = 0; w < 3; ++w) {
std::cout << (*it)[w] << ' ';
}
}
As you can imagine, you'll need to assume the same amount of words for each sentence. If you don't want that, you can create a new data structure that also holds the amount of words per sentence together with the vector of sentences.
Being a masochist is a good exercise, but for practical purposes you should probably switch to vectors so that you can iterate over the words more easily.