Vector of pairs to map - c++

I have a little problem.
I have a vector of pairs patternOccurences. The pairs are <string,int>, where string is the pattern(name) and int the index where it appears. My problem is that patternOccurences has multiple pairs with the same .first(same pattern) but different int values.
For example: The vector has 10 entries. 5 of pattern "a" and 5 of pattern "b". all have different indices. Now i want to have a map (or something similar) so that i have a vector/list with each pattern(in my example "a" and "b") as a key and a vector of their indices as the value. The indices are in the different pairs in my vector of pairs and i want all indices for pattern "a" in a int vector as value for key "a".
I tried the following:
std::map<std::string,std::vector<int>> occ;
for(int i = 0;i<patternOccurences.size();i++){
if(occ.find(patternOccurences.at(i).first)==occ.end()){
occ[patternOccurences.at(i).first]=std::vector<int>(patternOccurences.at(i).second);
}
else{
occ[patternOccurences.at(i).first].push_back(patternOccurences.at(i).second);
}
}
patternOccurences is the vector of pairs and occ the desired map. First i check if there is already an entry for the string(pattern) and if not i create one with a vector as value. If there is already one I try to push_back the vector with the index. However it doesnt seem to be working right. For the first pattern i get a vector with 0 only as values and for the second there are only 3 indices which are right and the other ones are 0 as well.
I hope you can help me.
Kazoooie

You are calling the constructor for the vector in the wrong way:
std::vector<int>(patternOccurences.at(i).second);
This creates a vector with N default constructed elements, not a vector with one element with value N. You need:
std::vector<int>(1, patternOccurences.at(i).second);
That should fix the problem, but your code doesn't have to be that complicated. The following would work just fine:
for(int i = 0;i<patternOccurences.size();i++){
occ[patternOccurences.at(i).first].push_back(patternOccurences.at(i).second);
}
or with C++11, the even simpler:
for(auto& p:patternOccurences) {
occ[p.first].push_back(p.second);
}

What you are asking for already exists in STL and it's called std::multimap (and std::unordered_multimap).
Take a look here. Basically it's a map which allows more values to have the same key.
std::multimap<std::string, int> occ;
occ.insert(std::pair<std::string,int>("foo", 5));
occ.insert(std::pair<std::string,int>("foo", 10));
std::pair<std::multimap<std::string,int>::iterator, std::multimap<std::string,int>::iterator> group = occ.equal_range("foo");
std::multimap<std::string,int>::iterator it;
for (it = ret.first; it != ret.second; ++it) {
..
}

Change this statement
occ[patternOccurences.at(i).first]=std::vector<int>(patternOccurences.at(i).second);
to
occ[patternOccurences.at(i).first]=std::vector<int>(1, patternOccurences.at(i).second);

Related

C++ map showing values as 0 for all keys

I am inserting number from a vector in map with key as the number and value as it's (index + 1).
But when I print the contents of the map, value being shown is 0 though I pass interger i.
// taking input of n integers in vector s;
vector<int> s;
for(int i=0;i<n;i++){
int tmp;cin>>tmp;
s.push_back(tmp);
}
//creating map int to int
map<int,int> m;
bool done = false;
for(int i=1;i<=s.size();i++){
//check if number already in map
if (m[s[i-1]]!=0){
if (i-m[s[i-1]]>1){
done = true;
break;
}
}
// if number was not in map then insert the number and it's index + 1
else{
m.insert({s[i-1],i});
}
}
for(auto it=m.begin();it!=m.end();it++){
cout<<endl<<it->first<<": "<<it->second<<endl;
}
For input
n = 3
and numbers as
1 2 1 in vector s, I expect the output to be
1: 1
2: 2
but output is
1: 0
2: 0
Why 0? What's wrong?
Your code block following the comment:
// check if number already in map
is logically faulty, because operator[] will actually insert an element, using value initialisation(a), if it does not currently exist.
If you were to instead use:
if (m.find(s[i-1]) != m.end())
that would get rid of this problem.
(a) I believe(b) that value initialisation for classes involve one of the constructors; for arrays, value initialisation for each item in the array; and, for other types (this scenario), zero initialisation. That would mean using your method creates an entry for your key, with a zero value, and returns that zero value
It would then move to the else block (because the value is zero) and try to do the insert. However, this snippet from the standard (C++20, [map.modifiers] discussing insert) means that nothing happens:
If the map already contains an element whose key is equivalent to k, there is no effect.
(b) Though, as my kids will point out frequently, and without much prompting, I've been wrong before :-)
std::map::operator[] will create a default element if it doesn't exist. Because you do m[s[i-1]] in the if condition, m.insert({s[i-1],i}); in else branch will always fail.
To check if key is already present in map use either find(), count() or contains() (if your compiler supports C++20)
//either will work instead of `if (m[s[i-1]]!=0)`
if (m.find(s[i-1]) != m.end())
if (m.count(s[i-1]) == 1)
if (m.contains(s[i-1])) //C++20

How can I insert multiple numbers to a particular element of a vector?

I am quite new to C++ and vector. I am calculating two things say 'i' and 'x' and I want to add 'x' that belongs to a particular vector element 'i'. I learned that if I have one 'x' value, I can simply do that by 'vec.at(i) = x'. But what if I want to add several 'x' values to a particular 'i' index of a vector?
Let's try to make it clear: Let's say I am searching for number '5' and '3' over a list of numbers from 1 to 10 (5 and 3 can occur multiple times in the list) and each time I am looking for number 5 or 3 that belong to index '2' of 'vec' I can do 'vec.at(2) = 5' or 'vec.at(2) = 3'. Then what if I have two '5' values and two '3' values so the sum of the index '2' of 'vec' will be '5+5+3+3' = 16?
P.S: using a counter and multiply concept will not solve my problem as the real problem is quite complicated. This query is just an example only. I want a solution within vector concept. I appreciate your help in advance.
If you know how many indices you want ahead of time, then try std::vector<std::vector<int>> (or instead of int use double or whatever).
For instance, if you want a collection of numbers corresponding to each number from 0 to 9, try
//This creates the vector of vectors,
//of length 10 (i.e. indices [0,9])
//with an empty vector for each element.
std::vector<std::vector<int>> vec(10, std::vector<int>());
To insert an element at a given index (assuming that there is something there, so in the above case there is only 'something there' for elements 0 through 9), try
vec.at(1).push_back(5);
vec.at(1).push_back(3);
And then to take the sum of the numbers in the vector at index 1:
int sum = 0;
for (int elem : vec.at(1)) { sum += elem; }
//sum should now be 8
If you want it to work for arbitrary indices, then it should be
std::map<int, std::vector<int>> map;
map[1].push_back(5); //creates an empty vector at index 1, then inserts
map[1].push_back(3); //uses the existing vector at index 1
int sum = 0;
for (int elem : map.at(1)) { sum += elem; }
Note that for std::vector and std::map, using [] do very different things. Most of the time you want at, which is about the same for both, but in this very specific case, [] for std::map is a good choice.
EDIT: To sum over every element in every vector in the map, you need an outer loop to go through the vectors in the map (paired with their index) and an inner loop like the one above. For example:
int sum = 0;
for (const std::pair<int, std::vector<int>>& index_vec : map) {
for (int elem : index_vec.second) { sum += elem; }
}

C++ Map get first element when second element is X

I have a C++ map called buttonValues as shown below.
map<int, int> buttonValues;
I put some data into my map as shown below.
buttonValues.insert(std::pair<int, int>(0, 1));
buttonValues.insert(std::pair<int, int>(1, 3));
buttonValues.insert(std::pair<int, int>(2, 0));
What I want to do is search for value 0 in the second column and if 0 is found in the second column, return the value in the first column. In this example, the value I would like to be returned is 2. So far I believe I can search for 0 in the second column with this:
buttonValues.find(0)->second
However, how do I get the value corresponding in the first column?
Thanks
Calum
buttonValues.find(0)->second will give you the value ("2nd column") corresponding to key 0. In your example, it will return 1. You need to iterate over the map and look for values = 0 and then return the key:
for (const auto& keyval : buttonValues) // Look at each key-value pair
{
if (keyval.second == 0) // If the value is 0...
{
return keyval.first; // ...return the first element in the pair
}
}
You can put this in a function. Note that a map has unique keys but not necessarily unique values. So you should probably handle the case where you have multiple keys for which the value is 0.
Something like this:
for ( auto X : map_name )
if ( X.second == 0 )
return X.first;
std::pair<> holds values of first and second columns in your map. You can just iterate through all pairs and check second values for what you want.
Actually from performance's perspective, it is not suggested to search key by value from a map, the time complexity would be linear time O(N). If you search value by key from a map, it would be 'O(logN)'. You can consider to build a reverse map or multimap, or even unordered_map / unordered_multimap depends on your use case.

How to order the elements of a map using its values if keys are same?

I have a situation where I have to store marks of some students in descending order. So created a map<marks,string>. As a map stores data in the form of heap, always highest mark will be on top. But the problem arises when I have same mark for two students and I have to then rank them on the basis of their name (Alphabetical order considering their name's first letter).
What I did: I separated the marks and names into another map this time with key as name and mark as value. And then printed the new map in the reverse way. And then continued from where I left in the old map. But this requires creation of extra map and involves lot of processing.
My question Is there a better way to do this?
You could create a map<marks, list<string> > or something similar to store all names for one mark there. Lists come with a sort method, so you can sort all lists that have more than one entry.
If you insert them all at a time, you can
create a list if the key is not present
append the name to the mark's list
iterate over all keys
sort the value if larger than 1
print.
What I would do is to have a map like this :
map<marks, vector<string>>
As this, each mark is binded to a sorted array of students. Everytime you want to insert a new student into a vector, you simply need to find the right index to keep your vector sorted (Dichotomic search is the fastest)
You can also use list<string> instead of vector
You may just use set<pair<mark, string>/*optional appropriate-comparator*/>. It'll sort using marks first, then names.
It'll still be O(log n) for sorted insertion contrary to sorted vector solution
Basically you need a Map that can store duplicate keys(int) in non-ascending order and values(string) in non-descending order.
I have a situation where I have to store marks of some students in descending order.
Use std::greater so that the keys will be stored in descending order:
std::map<int, string, std::greater<int> > m;
But the problem arises when I have same mark for two students.
Use Multimap instead of Map. Multimap stores duplicate keys and that is its specialty.
std::multimap<int, string, std::greater<int> > m;
I have to then rank them on the basis of their name (Alphabetical order considering their name's first letter).
Store the value of identical-keys in map in a vector > sort the vector > insert the sorted vector back into map.
Note that, inside multimap, you don't need to worry about changing the keys or its order. Values are the only ones whose order is going to be changed (that too only within same-valued-key).
Demo
multimap<int, string, greater<int> >:: iterator it = m.begin(), st; //Stores duplicate keys(marks).
vector<string> vec;
int lastKey = INT_MAX;
while(it != m.end())
{
if(it->first < lastKey)
{
//Sort the Names
sort(vec.begin(), vec.end());
for(int i = 0; i < vec.size(); i++){
st->second = vec[i];
st++;
}
st = it;
vec.clear();
}
vec.push_back(it->second); //Insert name
lastKey = it->first; //marks of last inserted name.
it++;
}
//Do same for the last(lowest valued key in map) key.
sort(vec.begin(), vec.end());
for(int i = 0; i < vec.size(); i++){
st->second = vec[i];
st++;
}

How to add an element to a vector in a map

Right now my code is looping through a text file and adding to a map the kgrams and characters that follow. The keys are strings and the values are vectors. But how do I add an element to the vector associated with the key?
Here is the code for adding that value, but it doesn't seem to be working.
for (int i = 0; i < wrapAround.length(); i++)
{
temp = i + order;
kgram[wrapAround.substr(i, temp)].add(temp++); //add value to the vector associated with key
}
I have an if statement later on to see if the values later on are in the map, and it never is true... I am not sure why.
The second argument to string::substr in C++ is length and not ending position.
Try accessing the k-gram like this instead: wrapAround.substr(i, order)
I'll leave the substring indexing up to you but to add items to a C++ vector you use .push_back() or .push_front():
kgram[..].push_back(temp++);