Print size of a vector through an iterator - c++

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)

Related

Checking for duplicate values associated with key in map?

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?

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

C++ using cdb_read returns extra characters on some reads

I am using the following function to loop through a couple of open CDB hash tables. Sometimes the value for a given key is returned along with an additional character (specifically a CTRL-P (a DLE character/0x16/0o020)).
I have checked the cdb key/value pairs with a couple of different utilities and none of them show any additional characters appended to the values.
I get the character if I use cdb_read() or cdb_getdata() (the commented out code below).
If I had to guess I would say I am doing something wrong with the buffer I create to get the result from the cdb functions.
Any advice or assistance is greatly appreciated.
char* HashReducer::getValueFromDb(const string &id, vector <struct cdb *> &myHashFiles)
{
unsigned char hex_value[BUFSIZ];
size_t hex_len;
//construct a real hex (not ascii-hex) value to use for database lookups
atoh(id,hex_value,&hex_len);
char *value = NULL;
vector <struct cdb *>::iterator my_iter = myHashFiles.begin();
vector <struct cdb *>::iterator my_end = myHashFiles.end();
try
{
//while there are more databases to search and we have not found a match
for(; my_iter != my_end && !value ; my_iter++)
{
//cerr << "\n looking for this MD5:" << id << " hex(" << hex_value << ") \n";
if (cdb_find(*my_iter, hex_value, hex_len)){
//cerr << "\n\nI found the key " << id << " and it is " << cdb_datalen(*my_iter) << " long\n\n";
value = (char *)malloc(cdb_datalen(*my_iter));
cdb_read(*my_iter,value,cdb_datalen(*my_iter),cdb_datapos(*my_iter));
//value = (char *)cdb_getdata(*my_iter);
//cerr << "\n\nThe value is:" << value << " len is:" << strlen(value)<< "\n\n";
};
}
}
catch (...){}
return value;
}
First, I am not familiar with CDB and I don't believe you include enough details about your software environment here.
But assuming it is like other database libraries I've used...
The values probably don't have to be NUL-terminated. That means that casting to char* and printing it will not work. You should add a 0 byte yourself.
So malloc cdb_datalen + 1 and set the last character to 0. Then print it.
Better yet, use calloc and it will allocate memory already set to zero.

how to look up hash_map in C++?

Here's what I have, I am new to C++ so I am not sure if this is right...
typedef pair<string, int>:: make_pair;
hash_map <string, int> dict;
dict.insert(make_pair("apple", 5));
I want to give my hash_map "apple", and I want to get back 5. How do I do it?
hash_map is not standard C++ so you should check out the documentation of whatever library you're using (or at least tell us its name), but most likely this will work:
hash_map<string, int>::iterator i = dict.find("apple");
if (i == dict.end()) { /* Not found */ }
else { /* i->first will contain "apple", i->second will contain 5 */ }
Alternatively, if you know for sure that "apple" is in dict, you can also do: dict["apple"]. For example cout << dict["apple"]; will print out 5.
Also, why the typedef in your code? Can't you just use std::make_pair? And, it won't compile the way you wrote it (with the two leading colons)
Iterate your hashmap, vector, list and other structures:
for(hash_map<string,int>::iterator i = dict.begin(); i != dict.end(); i++)
{
cout << "key(string): " << i->first << ", value(int): " << i->second << endl;
}