Reputation: 684
I have two std::unordered_map
std::unordered_map<int, int> mp1;
std::unordered_map<int, int> mp2;
I need to find the intersection of key-value pairs and store it in another map of the form.
std::unordered_map<int, int> mp;
How can i do this??
Upvotes: 5
Views: 1791
Reputation: 117298
You could use std::set_intersection
to fill a new container containing the key
, value
pairs that exists in both maps. set_intersection
needs the ranges to be sorted (which is exactly what you won't get from an unordered_map
) so either, replace the unordered_map
s with map
or create temporary map
s (or temporary std::set<std::pair<int, int>>
s) before using set_intersection
.
I would recommend replacing your original unordered_map
s with ordered map
s for efficiency if you need intersections often:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <map>
#include <unordered_map>
#include <vector>
int main() {
std::map<int, int> mp1 {{1,0}, {2,0}, {3,0}};
std::map<int, int> mp2 {{0,0}, {2,0}, {3,0}};
// this can be unordered unless you plan to use it in an intersection later:
std::unordered_map<int, int> mp;
std::set_intersection(
mp1.begin(), mp1.end(),
mp2.begin(), mp2.end(),
std::inserter(mp, mp.begin())
);
for(auto[key, val] : mp) {
std::cout << key << ',' << val << '\n';
}
}
Possible output:
3,0
2,0
If you want to stay with unordered_map
s and to not have to create temporary set
s or map
s, you can just replace set_intersection
above with a manual filler:
const auto& [min, max] = std::minmax(mp1, mp2,
[](auto& a, auto& b) {
return a.size() < b.size();
});
for(auto& [key, value] : min) { // iterate over the smallest map
auto fi = max.find(key); // find in the bigger map
if(fi != max.end() && fi->second == value)
mp.emplace(key, value); // add the pair if you got a hit
}
The reason for iterating over the smallest map is to keep the number of find
operations down to a minimum. Consider a case where one map contains 1 element and the other 1000000 elements. You then want 1 lookup and not 1000000.
A more generic solution could be making a function template out of it:
template<
class Key,
class T,
class Hash = std::hash<Key>,
class KeyEqual = std::equal_to<Key>,
class Allocator = std::allocator< std::pair<const Key, T> >
>
auto unordered_map_intersection(
const std::unordered_map<Key,T,Hash,KeyEqual,Allocator>& mp1,
const std::unordered_map<Key,T,Hash,KeyEqual,Allocator>& mp2)
{
std::unordered_map<Key,T,Hash,KeyEqual,Allocator> mp;
const auto& [min, max] = std::minmax(mp1, mp2,
[](auto& a, auto& b) {
return a.size() < b.size();
});
for(auto& [key, value] : min) { // iterate over the smallest map
auto fi = max.find(key); // find in the bigger map
if(fi != max.end() && fi->second == value)
mp.emplace(key, value); // add the pair if you got a hit
}
return mp;
}
Upvotes: 9
Reputation: 35
for(auto it=mp1.begin();it!=mp1.end();it++)
{
auto it1=mp2.find(it->first);
if(it1==mp2.end())
continue;
if((*it1)==(*it))
mp.insert(*it);
}
will make a map of <k,v> where pairs <k,v> are in both mp1 and mp2.
Or faster
auto it1=mp1.begin();
auto it2=mp2.begin();
while(it1!=mp1.end() && it2!=mp2.end())
{
if((*it1)==(*it2))
{
mp.insert(*it1);
it1++;
it2++;
continue;
}
if((*it1)<(*it2))
it1++;
else
it2++;
}
Upvotes: 3
Reputation: 466
Here's a manual solution using std :: set
:
#include <iostream>
#include <set>
#include <unordered_map>
std :: unordered_map <int, int> intersection (std :: unordered_map <int, int> m1, std :: unordered_map <int, int> m2)
{
std :: set <std :: pair <int, int>> s (m1.begin(), m1.end());
std :: unordered_map <int, int> i;
for (auto p: m2)
if (s.find (p) != s.end())
i.insert (p);
return i;
}
int main()
{
std :: unordered_map <int, int> m1 = { { 2, 3 }, { 5, 7 }, { 11, 5 }, { 6, 7 } };
std :: unordered_map <int, int> m2 = { { 21, 13 }, { 2, 3 }, { 6, 7 }, { 3, 2 } };
std :: unordered_map <int, int> i = intersection (m1, m2);
for (auto p: i)
std :: cout << p.first << ' ' << p.second << '\n';
return 0;
}
Output:
6 7
2 3
Upvotes: 0