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?
Related
I've noticed that unordered_map::equal_range upper_bound (first) returns end if the passed key is less than map's first one
#include <iostream>
#include <map>
#include <tr1/unordered_map>
using namespace std;
int main ()
{
{
std::map<char,int> mymap;
mymap['c'] = 60;
std::map<char,int>::iterator itup = mymap.equal_range('a').first;
std::cout << "map::itup " << itup->first << std::endl;
}
{
tr1::unordered_map<char, int> mymap;
mymap['c'] = 60;
mymap['d'] = 70;
tr1::unordered_map<char, int>::iterator itlo = mymap.equal_range('a').first;
tr1::unordered_map<char, int>::iterator itup = mymap.equal_range('a').second;
cout << "unordered_map::itup " << (itup == mymap.end() ? "END" : "NOT END") << std::endl;
cout << "unordered_map::itlo " << (itlo == mymap.end() ? "END" : "NOT END") << std::endl;
}
return 0;
}
Output is:
map::itup c
unordered_map::itup END
unordered_map::itlo END
Note that the behavior is different for map and unordered_map - any reasons for that or is this a problem in unordered_map?
This happens because an unordered_map is, not too surprisingly, unordered.
See §22.2.7 [unord.req], Table 70, regarding the requirements on equal_range:
Returns: A range containing all elements with keys equivalent to k.
Returns make_pair(b.end(), b.end()) if no such elements exist.
This is different from the requirements on an ordered associative container, like std::map, where equal_range is defined in terms of lower_bound and upper_bound.
std::unordered_map doesn't have lower_bound and upper_bound, for obvious reasons.
You asked for a range consisting of all elements in your unordered_map whose key is 'a'. Your unordered map contains no such elements. So, the range is empty.
The same is true of the map case. However, the way in which this condition is signified differs by container (although not really; keep reading). The containers std::map and std::unordered_map are not the same thing (hence they have different names). The former is ordered, whereas the latter is not, so for logical implementation reasons it works slightly differently:
unordered_map
Return value
std::pair containing a pair of iterators defining the wanted range. If there are no such elements, past-the-end (see end()) iterators are returned as both elements of the pair.
map
Return value
std::pair containing a pair of iterators defining the wanted range: the first pointing to the first element that is not less than key and the second pointing to the first element greater than key.
If there are no elements not less than key, past-the-end (see end()) iterator is returned as the first element. Similarly if there are no elements greater than key, past-the-end iterator is returned as the second element.)
This difference does not matter. In either case you should simply iterate (first, second] to examine the elements (if any exist) in your range, as you would with any iterator range.
In your code, you didn't examine both parts of the pair returned in your map case. If you do then you'll find that first == second (again, signifiying an empty range).
Your map code effectively dereferences the "past-the-end" iterator of the returned range.
#include <iostream>
#include <map>
#include <unordered_map>
using namespace std;
int main ()
{
{
std::map<char,int> mymap;
mymap['c'] = 60;
std::map<char, int>::iterator itlo = mymap.equal_range('a').first;
std::map<char, int>::iterator itup = mymap.equal_range('a').second;
// This compares each range extent to the map's end, which is not really useful
cout << "map::itup " << (itup == mymap.end() ? "END" : "NOT END") << '\n';
cout << "map::itlo " << (itlo == mymap.end() ? "END" : "NOT END") << '\n';
// This examines the range itself
cout << "map range empty: " << (itlo == itup ? "YES" : "NO") << '\n';
cout << "map range size: " << std::distance(itlo, itup) << '\n';
}
{
std::unordered_map<char, int> mymap;
mymap['c'] = 60;
mymap['d'] = 70;
std::unordered_map<char, int>::iterator itlo = mymap.equal_range('a').first;
std::unordered_map<char, int>::iterator itup = mymap.equal_range('a').second;
// This compares each range extent to the map's end, which is not really useful
cout << "unordered_map::itup " << (itup == mymap.end() ? "END" : "NOT END") << std::endl;
cout << "unordered_map::itlo " << (itlo == mymap.end() ? "END" : "NOT END") << std::endl;
// This examines the range itself
cout << "unordered_map range empty: " << (itlo == itup ? "YES" : "NO") << '\n';
cout << "unordered_map range size: " << std::distance(itlo, itup) << '\n';
}
}
// Output:
//
// map::itup NOT END
// map::itlo NOT END
// map range empty: YES
// map range size: 0
// unordered_map::itup END
// unordered_map::itlo END
// unordered_map range empty: YES
// unordered_map range size: 0
(live demo)
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;
}
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;
}
}
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 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)