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;
}
}
Related
I'm struggling with outputting unique values of a map<string, vector<string>> I have. Right now, I have a map, and iterate through it, with the goal of outputting only unique values associated with the specified key.
I do need to keep the duplicate values, or else I'd just remove the dups :)
After looking at this post, my set up is like the following:
for( const auto& pair : myMap ){
for( std::size_t i = 0; i < pair.second.size(); ++i ) {
bool notMatch = (pair.second[i] != pair.second[i+1]){
if (pair.first == key && notMatch){
cout << key << " : ";
cout << pair.second[i] << " - at index - " << i << "\n";
}
}
}
I then get an output along the lines of :
"key : value - at index - 6"
"key : value - at index - 10"
My initial thought was that one of the elements might have some extra characters or something, which would make sense as to why the duplicate elements are not being seen as equal.
But when doing a simple check of -
if (pair.second[6] == pair.second[10]){
cout << "They are equal";
} else {
cout << "They are NOT equal";
}
It confirms and returns that the two elements are in fact equal. Since the elements are equal, I'm struggling to understand why bool notMatch = (pair.second[i] != pair.second[i+1]) does not consider them to be equal.
Apologies if this was posted incorrectly, I'll edit if necessary.
Thanks for your help
Building on #Tzalumen's comment, you could insert the values in a set or unordered set and compare the size to the original vector:
for(const auto& pair : myMap){
unordered_set<string> s(pair.second.begin(), pair.second.end());
if (s.size() == pair.second.size()) {
cout << "value has unique elements" << endl;
} else {
cout << "value has duplicate elements" << endl;
}
}
If the set's size is smaller than the vector's size, you know the vector has duplicates.
If you don't want duplicates anywhere, why not have a std::map<std::string, std::set<std::string>> in the first place?
Good morning,
I have a multiple map defined in this way
multimap<string, string> H_test;
with value insered from user(multiple book written by the same author in a random order)
H_test.insert(pair<string, string>(author, book));
I would like to have as output something like that :
Author I // one author
Book A // all book written by this author
Book B
Author II
Book K
Author III
Book C
Book D
Book E
Using this code
for (multimap<string,string>::iterator it = H_test.begin(); it != H_test.end(); it++)
{
//if ()
{
pair <multimap<string, string>::iterator, multimap<string, string>::iterator> ret;
ret = H_test.equal_range(it->first);
std::cout << it->first << endl;
for (multimap<string, string>::iterator sd = ret.first; sd != ret.second; sd++)
{
cout << "\t" << sd->second << endl;
}
}
}
I obtain this output
Author I
Book A
Book B
Author I
Book A
Book B
Author II
Book K
Author III
..
In my opinion, it prints the name of first key (and all the books which have the same author) and then it goes to the second nome (which has the same name), and re-write everything.
Do you know how to solve it?
Thanks
upper_bound is your friend. You'd want to use it something like this:
auto start = begin(H_test);
while (start != end(H_test)) {
auto finish = H_test.upper_bound(start->first);
cout << "Author: " << start->first << endl;
for_each(start, finish, [](const auto& i) { cout << "Book: " << i.second << endl; });
start = finish;
}
Live Example
Note: You can use cbegin and cend instead of if H_test is const.
EDIT:
It helps me to conceptualize a multimap as a sorted array of pairs, where the first element in the pair is the key and the second element is the value. Here's effectively what we'll do:
Iterate through all the pairs using start as our iterator, beginning at the first pair: auto start = begin(H_test) and going to the last pair in the multimap: while(start != end(H_test))
Next we want to find the range of pairs in the container that share the same key, for that we'll find an iterator pointing one past the end using the upper_bound function that I linked the details about above: auto finish = H_test.upper_bound(start->first)
We will print the current author who's books we just found in the range: cout << "Author: " << start->first << endl
Then we'll print each book in the range defined by start to finish using for_each and a lambda to only print the value of the pair then move to the next line: for_each(start, finish, [](const auto& i) { cout << "Book: " << i.second << endl; });
Finally we'll set start to the beginning of the next range, which will be printed next time through the loop: start = finish
Simply iterate through the multimap and since all the elements with the same author are grouped together, you just need to detect a change in author to know you have moved on to the next author at which time you print the name of the author. Preset the lastAuthor to something that won't potentially match a real author. This executes in O(n)
string lastAuthor = "**SENTINEL**";
for (const auto& elem : H_test)
{
if(lastAuthor != elem.first)
{
cout << "Author: " << elem.first << '\n';
lastAuthor = elem.first;
}
cout << "Book: " << elem.second << '\n';
}
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.
I'm trying to build a relational database for the class I'm in.
what's happening is that when I process my "Facts" and "Queries" input, I create a new relation object. And then I print them out. If I run one at a time they process just fine, but if I run them back to back, the second one modifies the contents of the vector of tokens within the other relation object.
Database.h
class Database
{
private:
datalogProgram program;
Relation theSchemes;
Relation theFacts;
std::vector<Token> FactsOrder;
public:
Database(datalogProgram input);
Database();
~Database();
Relation processSchemes(datalogProgram processme);
Relation processFacts(datalogProgram processme);
};
Database.cpp
And I apologize for all of the cout's I've been trying to debug this things for hours!
#include "Database.h"
#include <sstream>
Database :: Database(datalogProgram input)
{
// So first I will make a map with relations representing the Schemes Facts and Queries
// Thus I will have a database of schemes facts and queries, rules excluded and ignored for now.
program = input;
theSchemes = processSchemes(program);
theFacts = processFacts(program);
// just checking on progress.
std::cout << "SCHEMES" << std::endl;
theSchemes.printRelation();
std::cout << "FACTS" << std::endl;
theFacts.printRelation();
}
Database :: Database() {}
Database :: ~Database() {}
Relation Database :: processSchemes(datalogProgram input)
{
Relation temp;
// LETS START WITH SCHEMES
std::cout << "processing schemes" << std::endl;
std::vector<Scheme>* schemes = input.returnSchemeList();
// Process First Scheme
// Populate this first vector with ID's from schemes.
// std::vector<Token*> firstTuple;
std::vector<Token*> firstTuple;
std::vector<Token> idListONE;
firstTuple.push_back(input.returnFirstScheme()->returnFirstID());
// std::vector<Token> idListONE;
idListONE = input.returnFirstScheme()->returnCLEANidLIST();
for(int i = 0; i < input.returnFirstScheme()->returnCLEANidLIST().size(); i++)
firstTuple.push_back(&idListONE[i]);
temp = *new Relation(input.returnFirstScheme()->returnName(), firstTuple);
// NOW I NEED TO PROCESS THE REST OF THE SCHEMES
//Take a scheme off of the list, and work on it just like I did above.
for(int j = 0; j < schemes->size(); j++) {
// Populate this first vector with ID's from schemes.
std::vector<Token*> first;
first.clear();
first.push_back(schemes->at(j).returnFirstID());
std::vector<Token> idLista;
idLista.clear();
idLista = schemes->at(j).returnCLEANidLIST();
for(int i = 0; i < schemes->at(j).returnCLEANidLIST().size(); i++)
first.push_back(&idLista[i]);
temp.relationInsert(schemes->at(j).returnName(), first);
}
return temp;
//
// At this point I shoudl have a map with "Schemes" pointing to Relation Objects.
// I want to verify that this is working, so print out all data collected so far.
}
Relation Database :: processFacts(datalogProgram input)
{
Relation temporary;
// NOW WE PROCESS FACTS
// Order does matter, so I will create a vector to use as a key.
std::cout << "procesing facts" << std::endl;
std::vector<Fact>* facts = input.returnFactList();
std::string OUT2;
std::ostringstream convert2;
convert2 << facts->size();
OUT2 = convert2.str();
std::cout << "THE NUMBER OF FACTS IS " << OUT2 << std::endl;
// NOW I NEED TO PROCESS THE REST OF THE
//Take a scheme off of the list, and work on it just like I did above.
std::vector<Token*> firstTuple;
std::vector<Token> idListONE;
for(int j = 0; j < facts->size(); j++) {
std::cout << "NEW ITERATION:" << std::endl;
if(j==0) {
std::cout << "processing first fact" << std::endl;
// is the first Fact!
firstTuple.clear();
std::cout << "processing first fact --> tuple" << std::endl;
firstTuple.push_back(facts->at(j).returnFirstString());
idListONE.clear();
std::cout << "FIRST STRINGLIST" << std::endl;
idListONE = *facts->at(j).returnCleanStringList();
for(int i = 0; i < idListONE.size(); i++) {
std::cout << "FIRST STRING ITER" << std::endl;
firstTuple.push_back(&idListONE[i]);
}
FactsOrder.push_back(*facts->at(j).returnName());
std::cout << "creating first fact" << std::endl;
temporary = Relation(facts->at(j).returnName(), firstTuple);
} else {
std::cout << "processing A fact (ITER)" << std::endl;
// Populate this first vector with ID's from schemes.
std::vector<Token*> first;
first.clear();
std::cout << "processing fact, firststring (ITER)" << facts->at(j).returnFirstString()->getTokensValue() << std::endl;
first.push_back(facts->at(j).returnFirstString());
std::vector<Token> idLista;
idLista.clear();
std::cout << "getting stringlist (ITER)" << std::endl;
idLista = *facts->at(j).returnCleanStringList();
for(int i = 0; i < idLista.size(); i++) {
std::cout << "processing stringlist (ITER) ITER" << std::endl;
first.push_back(&idLista[i]);
}
FactsOrder.push_back(*facts->at(j).returnName());
std::cout << "adding fact" << std::endl;
temporary.relationInsert(facts->at(j).returnName(), first);
}
}
return temporary;
}
relation.cpp
Just so you can see it
Relation :: Relation(Token* key,std::vector<Token*> tuple)
{
std::pair<Token*,std::vector<Token*> > mypair (key,tuple);
contents.insert(mypair);
}
Relation :: Relation() {}
Relation :: ~Relation() {}
void Relation :: relationInsert(Token* key,std::vector<Token*> tuple)
{
std::pair<Token*,std::vector<Token*> > mypair (key,tuple);
contents.insert(mypair);
}
void Relation :: printRelation()
{
std::cout << "PRINT RELATION CALLED" << std::endl;
std::multimap<Token*,std::vector<Token*> >::iterator mapIT;
for(mapIT = contents.begin() ; mapIT != contents.end() ; mapIT ++) {
std::cout << "Key: " << mapIT->first->getTokensValue() "\nValues:" << std::endl;
for(int x = 0; x< mapIT->second.size() ; x++)
std::cout << " " << mapIT->second.at(x)->getTokensValue() << std::endl;
}
}
To solve your problem you must figure out object / pointer ownership in your code. Relation holds a relation between a pointer to Token and a list of other pointer to Tokens. It is ok to keep Token* rather then a copy of Token. (Especially if tokens can be large words you don't want to copy). But who "owns" and manages the tokens?
Lets look at an example
std::vector<Token*> firstTuple;
std::vector<Token> idListONE;
idListONE is a vector to actual Tokens. It is a function local variable so it will be discarded when we exit the function.
firstTuple is a vector to pointers of Tokens.
You push into it in the following manner:
firstTuple.push_back(&idListONE[i]);
So firstTuple tokens are pointers to the internal tokens inside idListONE. That might be valid but you must remember that as soon as idListONE is released or its memory is changed (its size increased for instance) firstTuple becomes invalid, because it will now point at memory that was released and using it may have undefined results and will likely crash the program.
Actually a few lines later you make that mistake:
temporary = Relation(facts->at(j).returnName(), firstTuple);
temporary is a Relation that holds a list to pointer of Tokens. It copies the list that means that it copies the token pointers. However the pointers it copies are to Tokens that belong to idListONE as soon as you exit the function idListONE is released and the pointers inside the Relation are no longer valid and using them is likely one source of the problems you are seeing. There might be additional problems like this in the code
In general there seems to be a lot of confusion about working with pointers vs working with objects.
Look at the following statement:
temp = *new Relation(input.returnFirstScheme()->returnName(), firstTuple);
new Relation(...) will allocate memory on the heap and initialize a Relation.
temp = *<ptr> will use operator= to copy the content on the right into temp. The Relation on the heap is forgotten and its memory is leaked.
Another example:
idListONE.clear();
std::cout << "FIRST STRINGLIST" << std::endl;
idListONE = *facts->at(j).returnCleanStringList();
first you clear idListONE then you use the operator= to overwrite it with a new list.
Why did you clear a list you are writing over?
Why do you return a pointer to a list from returnCleanStringList()? instead of a copy list or a const ref to an internal list? If you decided returnCleanStringList() should return a list by pointer rather then by value then why is the first thing you do is copying it?
Finally you really should choose one style and conform to it. In the long run it makes code clearer.
If you Camelize variable names then always do: idListONE -> idListOne
Also avoid members like 'idListONE', do you really need a different variables for the first index?
I'm trying to print the size of a vector. Sounds easy, but the vector is in a map.
Currently I have an iterator on a map looking like this:
map<string, vector<map<vector<string> , vector<string> > > >::iterator it;
I am trying to display the size like this:
EDIT:
The iterator is intialised like this: it = csvMap.find(commandList.at(lineCount));
cout<<"Size of vector in Map after modifying: " << it->second.size() <<"\n"<<endl;
It's not working, the program crashes.
I think a way would be to make a temp vector and fill it with the value it->second;
But just to get the size is kind of wasting space isn't it?
Is there a better way to do so?
Thanks in advance!
EDIT2: removed old code
EDIT 3: new code:
map<vector<string> , vector<string> > parameterMap;
parameterMap.insert(pair<vector<string> , vector<string> > (
part1_input, part2_output));
map<string, vector<map<vector<string> , vector<string> > > >::iterator it;
cout<<"\nSize of CSV Map before modifying: " << csvMap.size() <<endl;
//cout<<"Size of vector in CSV Map before modifying: " << it->second.size() <<"\n"<<endl;
if(csvMap.size() == 0)
{
/*
* csvMap is empty -> no need to search for something. Just insert the fist entries
*/
listedParameterMap.insert(listedParameterMap.end(), 1, parameterMap);
csvMap.insert(pair<string, vector<map<vector<string> ,
vector<string> > > > (commandList[lineCount],
listedParameterMap));
cout<<"CSV Map size: " << csvMap.size() <<endl;
}
else
{
/*
* Search if the Command is already available, if not,
* add it to the map with its corresponding list of maps (in/output values)
* find returns map::end if key is not found
*/
cout<<"Checking if: " << commandList.at(lineCount) << " is already in the list \n" << endl;
it = csvMap.find(commandList.at(lineCount));
if (it == csvMap.end())
{
/*
* it = csvMap.end() is true
* The command isn't found
*/
cout<< commandList.at(lineCount) << " command not available. Inserting it! \n" << endl;
listedParameterMap.insert(listedParameterMap.end(), 1, parameterMap);
csvMap.insert(pair<string, vector<map<vector<string> ,
vector<string> > > > (commandList[lineCount],
listedParameterMap));
}
else
{
/*
* it != csvMap.end()
* The command is found. Append the parameterMap to the vector in the map
*/
cout<< commandList.at(lineCount) << " is already in the list! Appending parameters on pos: "<< it->second.size()-1<< "\n" << endl;
it->second.push_back(parameterMap);
}
}
cout<<"\nSize of CSV Map after modifying: " << csvMap.size() <<endl;
cout<<"Size of vector in CSV Map after modifying: " << it->second.size() <<"\n"<<endl;
I hope someone is still reading this...
I found now that it.second seems to be the problem on the first interation. But I don't get why.
Code snippet (also in the code above):
if(csvMap.size() == 0)
{
/*
* csvMap is empty -> no need to search for something. Just insert the fist entries
*/
listedParameterMap.insert(listedParameterMap.end(), 1, parameterMap);
csvMap.insert(pair<string, vector<map<vector<string> ,
vector<string> > > > (commandList[lineCount],
listedParameterMap));
cout<<"CSV Map size: " << csvMap.size() <<endl;
cout<<"listedParameterMap: " << listedParameterMap.size() <<endl;
cout<< commandList.at(lineCount) << " is already in the list! Appending parameters on pos: "<< it->second.size()<< "\n" << endl;
}
This seems not to work. Although its goning into it. Any idea why?
comanndList and listedParameterMap are as far as I see OK.
it = csvMap.find(commandList.at(lineCount));
if (it == csvMap.end()) {
cout << "not found\n";
}
else {
cout << "Size of vector in Map after modifying: " << it->second.size() << '\n';
}
Either when command isn't found or command is the last
No, the end iterator is not an item in the container.
string c = (*it).first;
Since this is after the iterator is the end iterator, you have undefined behavior when dereferencing it.
Your it is pointing to an invalid location. You need to initailize it with the map's iterator. Something like it = myMap.find("aaa"); //Replace it with actual key After doing the find you need to make sure you are having a valid iterator by checking it agains myMap.end().
EDIT
You are using uninitialized iterator here:
cout<<"Size of vector in CSV Map before modifying: " << it->second.size() <<"\n"<<endl;
Also, you can not dereference a iterator pointing csvMap.end(), it will result in crash again.
As per EDIT 3
You are still using the unitialized iterator / iterator pointing to end in if(csvMap.size() == 0) and if(it == csvMap.end()) case. You need to initialize the it with the return value of insert function like this:
it = csvMap.insert(....).first; in these cases.
Map iterators aren't invalidated unless you erased that specific element. That means that you did something else wrong.
Your collection is rather complex.
In order to check the size though you need to know there is an element there at all, i.e. find did not return "end" of your map. If it does, you cannot use the iterator returned.
Of course how you handle this is your own decision, eg return maybe -1 if the string was not found (with 0 indicating it was found but had no content).
Now you have edited your code I immediately spot a bug:
if (it == csvMap.end())
{
/*
* it = csvMap.end() > true
* Either when command isn't found or command is the last
*/
string c = (*it).first;
you cannot dereference end (which it is)