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

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.

Related

LEETCODE 349 what's the significance mp[x]=1;

I am looking at LeetCode problem 349. Intersection of Two Arrays:
Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must be unique and you may return the result in any order.
I solved it using a brute force algorithm, but then I found this solution:
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
map<int,int> mp;
vector<int> ans;
for(int x : nums1)
mp[x]=1;
for(int x: nums2)
if(mp[x]==1)
mp[x]++;
for(auto x: mp)
if(x.second>1)
ans.push_back(x.first);
return ans;
}
};
And now my question: What's the significance mp[x]=1; in that solution?
mp is a (hash)map, so it stores unique keys and associated values.
In this case the idea is to use the values in both input arrays as keys in such a map, and to associate the value 1 with a key when that key occurs in nums1 and to associate the value 2 with a key when that key occurs in nums1 and nums2. So that is a kind of "counter".
So mp[x] = 1 registers the value x (found in nums1) as key in that map and associates value 1 to it, to indicate that this key was found in nums1.
In the second loop, the map is used to verify whether a value x in nums2 is present as key in the map with an associated count of 1. If so, then the associated value is increased with mp[x]++ (so now it is 2).
The final loop iterates all the keys in the map and when it finds that the associated value is greater than 1 (i.e. it is 2), then that key value belongs in the result.

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 does insertion in an unordered_map in C++ work?

int main()
{
auto n=0, sockNumber=0, pairs=0;
unordered_map<int, int> numberOfPairs;
cin >> n;
for(int i=0; i<n; ++i)
{
cin >> sockNumber;
numberOfPairs.insert({sockNumber, 0}); // >>>>>> HERE <<<<<<
numberOfPairs.at(sockNumber) += 1;
if(numberOfPairs.at(sockNumber) % 2 == 0)
{
pairs += 1;
}
}
cout << pairs;
return 0;
}
This code counts the number of pairs in the given input and prints it. I want to know how the insert method of an unordered_map works. Every time I see a number, I've inserted it with a value '0'.
Does the insert method skip inserting the value '0' when it sees the same number again? How does it work?
Input -
9
10 20 20 10 10 30 50 10 20
Output -
3
Does the insert method skip inserting the value '0' when it sees the
same number again?
Yes, it does.
From the cpp.reference.com unordered_map :
Unordered map is an associative container that contains key-value
pairs with unique keys. Search, insertion, and removal of elements
have average constant-time complexity.
And from the cpp.reference.com unordered_map::insert :
Inserts element(s) into the container, if the container doesn't
already contain an element with an equivalent key.
How does it work?
I suppose that certain work principles depend much on the particular STL implementation.
Basically unordered_map is implemented as a hash table where elements are organized into the buckets corresponding to the same hash. When you try to insert a key-value pair key hash is computed. If there is no such hash in the hash table or there is no such key-value pair in the bucket corresponding to the computed hash then the new pair is inserted into the unordered_map.
A std::unordered_map holds unique keys as values. If you want to keep inserting the same key, then use std::unordered_multimap.
Also, you should realize that std::unordered_map::insert returns a value that denotes whether the insertion was successful.
if ( !numberOfPairs.insert({sockNumber, 0}).second )
{
// insertion didn't work
}
You could have used the above to confirm that the item wasn't inserted, since the same key existed already in the map.
unordered_map does not allow key duplicates, so if you are trying to use the .insert() method to insert the same key it will fail and skip that operation. However if you use unorderedMap[key] = value to insert a duplicate key, it will not skip but updating the value matching the key to the new value.

Vector of pairs to map

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

C++ Multimap manipulation

I have created a multimap as I have repeating keys. But I want do an efficient manipulation so that I can generate a new multimap with subsequent higher keys aligned. This is what I mean:
This is what I have:
key values
11 qwer
11 mfiri
21 iernr
21 ghfnfjf
43 dnvfrf
This is what I want to achive
key values
11 qwer,iernr
11 mfiri,iernr
21 iernr,dnvfrf
21 ghfnfjf,dnvfrf
43 dnvfrf
I have about 10 million entries so I am looking for something efficient.
In above value "qwer,iernr" is one string.
Here's a simple way to do it:
auto cur = map.begin();
auto next = map.upper_bound(cur->first);
for(; next != map.end(); next = map.upper_bound(cur->first))
{
for(; cur != next; ++cur)
{
cur->second += ", ";
cur->second += next->second;
}
}
... given a std::multimap<int, std::string> map;
However, any operation transforming 10m+ elements isn't going to be super fast.
Looks like straight-forward way would work fine. Map elements will be laid out in ascending order (assuming compare operator suits you). So just going through the equal ranges and modifying them with value of the element just after the range will do what you want.
Clone map (if you need the original), take first element, get equal_range() for its key, modify values with value of second iterator in the range (unless it is the last one). Get equal_range() for the key of second iterator. Repeat.
agree with Eugene ! also see following reference in terms of equal_range()
stl::multimap - how do i get groups of data?
To do this, you need to simply iterate through the map, while building the new map in order.
You can do this in two levels:
for (auto it=map.cbegin(); it != map.cend(); )
{
// The inner loop is over all entries having the same key
auto next_key_it=find_next_key_after(it);
for (; it != next_key_it; ++it) {
new_map.emplace_hint(new_map.end(), it->first, new_value(it->second, next_key_it));
}
}
The new_value function (or lambda) does the value transformation (or not, if the second parameter is map.end()).
The find_next_key_after(it) function returns the same as map.upper_bound(it->first), but could also be implemented as linear search for the first entry with different key.
It depends on your (expected) key distribution, which to use - if keys repeat a small, limited number of times, linear search is better; if the number of different keys is limited, with large equal key ranges, then upper_bound may be better.
For guaranteed complexity, linear search is better: The whole algorithm then has O(n) complexity. Which is as efficient as you can get.