Related
I'm looking for the faster method to extract the map values filtered by vector keys.
Below the standard code:
std::unordered_map<uint32, user> map;
std::vector<uint32> to_find;
std::vector<user> results;
auto it = map.begin();
while (it != map.end())
{
if (std::find(to_find.begin(), to_find.end(), it->first) == to_find.end())
results.push_back(it->second);
it++;
}
You can use std::binary_search(), which has a time complexity of O(log n) where n is the size of the vector to_find. This could be much better than using std::find() which has a linear time complexity.
DEMO
#include <algorithm>
#include <iostream>
#include <vector>
#include <unordered_map>
using user = int;
using uint32 = unsigned long int;
int main()
{
std::unordered_map<uint32, user> myMap = {{1,2},{3,5},{2,9},{4,7}};
std::vector<uint32> to_find = {1,3};
std::vector<user> results;
if(to_find.size() == 0) // if you have to_find vec size = 0
std::for_each(myMap.cbegin(), myMap.cend(), [&results](const auto& ele)->void
{
results.emplace_back(ele.second);
});
else
{
for(const auto& it: myMap)// binary_search; if not found add the value
if(!std::binary_search(to_find.begin(), to_find.end(), it.first))
results.emplace_back(it.second);
}
for(const auto& it: results) std::cout << it << std::endl;
}
If both your containers are sorted, a linear way is to use algorithm similar to std::merge:
template <typename KEY, typename VALUE>
std::vector<VALUE>
collect_absents(const std::map<KEY, VALUE>& map, const std::vector<KEY>& present)
{
std::vector<VALUE> res;
auto mit = map.begin();
auto vit = present.begin();
while (mit != map.end() && vit != present.end())
{
if (*vit < mit->first) {
++vit;
} else if (mit->first < *vit) {
res.push_back(mit->second);
++mit;
} else { // equal
++vit;
++mit;
}
}
for (; mit != map.end(); ++mit) {
res.push_back(mit->second);
}
return res;
}
Demo
I have an std::list which looks as follows (the x marks indicate some number less than 500)
x,x,x,x,503,x,x,x,510,x,x,x,502,x,x,x,x,x,x,600 - std::list<int> originallist
I am looking to split the list into a vector of lists std::vector<std::list<int> > as follows
1st element of vector: x,x,x,x,503
2nd element of vector: x,x,x,510
...
...
last element of vector: x,x,x,x,x,x,600
The code I have now is as follows:
list<int> templist; vector<list<int> > v;
for(list<int>::iterator lit=originallist.begin(); lit!=oriniallist.end(); ++lit) {
if (*lit > 500) {
templist.push_back(*lit);v.push_back(templist); templist.clear(); continue;
}
templist.push_back(*lit);
}
What is the most efficient way to achieve the above task in c++ without using templist. Any help is appreciated.
While this solution does use a temporary std::list, it allocates no list node elements, and does exactly 1 memory allocation in the C++03 case (the C++11 case does a logarithmic number of memory allocations on the size of the return value)
This is a C++03 solution. A C++11 solution can do this in one pass.
bool big_as_500( int x ) {return x>=500;}
std::vector< std::list< int > > do_stuff( std::list<int>& original_list ) {
// we have to do this, because resizing the return value involves lots of allocations
// and stuff in C++03, so make sure we get the size right by precalculating it:
std::size_t count = std::count_if( originallist.begin(), originallist.end(), big_as_500 );
std::vector< std::list< int > > result;
result.reserve(count+1);
typedef std::list<int>::const_iterator const_iterator;
std::list< int > current;
for(const_iterator it= originallist.begin(); it!=originallist.end();/*nothing*/) {
++it; // about to invalidate it! (or move lists)
current.splice( current.end(), originallist, originallist.begin() ); // O(1) no memory allocation
if (big_as_500(current.back())) {
result.push_back( std::list<int>() );
current.swap( result.back() );
}
}
// original problem does not specify what to do if the original list does not end
// with an element "big_as_500", so I'll just drop them
return result; // rely on NRVO to eliminate the copy here, if your compiler does not
// support it, take result as a reference parameter.
}
A C++11 solution:
std::vector< std::list< int > > do_stuff( std::list<int>& original_list ) {
std::vector< std::list< int > > result;
typedef std::list<int>::const_iterator const_iterator;
std::list< int > current;
for(const_iterator it= originallist.begin(); it!=originallist.end();/*nothing*/) {
++it;// about to become invalid/in wrong list
current.splice( current.end(), originallist, originallist.begin() ); // O(1) no memory allocation
if (current.back() >= 500) {
result.emplace_back( std::move(current) );
}
}
// original problem does not specify what to do if the original list does not end
// with an element "big_as_500", so I'll just drop them
return result; // will NRVO, or move, so no worries
}
in C++11, resizes are relatively cheap, so we are good.
Now, we could get really fancy in C++03 and emulate what C++11 does and do it all in one pass.
template<typename T, typename A>
void efficient_grow_by_1( std::vector<T,A>& make_one_bigger ) {
if (make_one_bigger.size()+1 > make_one_bigger.capacity() )
{
std::vector<T, A> swap_vec;
swap_vec.reserve( (make_one_bigger.size()+1)*5/3 );
for (std::vector<T, A>::iterator it = make_one_bigger.begin(); it != make_one_bigger.end(); ++it ) {
using std::swap;
swap_vec.push_back();
std::swap( *it, swap_vec.back() );
}
swap_vec.swap( make_one_bigger );
}
make_one_bigger.push_back();
}
void do_stuff( std::list<int>& original_list, std::vector< std::list< int > >& result ) {
typedef std::list<int>::const_iterator const_iterator;
std::list< int > current;
for(const_iterator it= originallist.begin(); it!=originallist.end();) {
++it;
current.splice( current.end(), originallist, originallist.begin() ); // O(1) no memory allocation
if (current.back()>=500) {
efficient_grow_by_1(result);
current.swap( result.back() );
}
}
// original problem does not specify what to do if the original list does not end
// with an element "big_as_500", so I'll just drop them
}
which is rather insane, so I'd advise upgrading your compiler.
The trick here is that we populate the 'temporary' list with a single-element-at-a-time splice. Because (most? many?) implementations of std::list::splice end up having to walk over the elements to count them (it is required in C++11, and common in C++03), doing it one at a time as we determine which elements we want to put into the next chunk is reasonably efficient. Each node comes directly from the input list, and is collected into the temporary list (no memory allocations).
Once we have built up this list, we directly swap it into the output vector of lists. This avoids any memory allocations, other than that which is required to hold the (relatively small) base data of the list.
In C++03, we either do a two-pass solution and pre calculate how big the output std::vector is, or we emulate C++11 move efficiency with careful growth and swap mechanics on the contained lists. It is possible that your std library implementation fakes this already, but I am unsure how common swap-resize optimization was in the old libraries.
Keeping things down to a single pass is probably worth the logarithmic number of allocations that the 2nd C++03 and C++11 solutions use: walking a std::list is an exercise in cache misses.
Third version
This version uses std::list::splice and moves iterator till delimiter is found or end() reached.
#include <iostream>
#include <list>
#include <vector>
std::vector< std::list<int> > & split( std::list<int> v,
int delim, std::vector< std::list<int> >& elems) {
auto it = v.begin();
while ( it != v.end()) {
std::list<int> l;
while ( it != v.end() && *it < delim) {
++it;
}
if( it != v.end()) {
l.splice( l.begin(), v, v.begin(), ++it);
it = v.begin();
} else {
l.splice( l.begin(), v, v.begin(), it);
}
elems.push_back( l);
}
return elems;
}
std::vector< std::list<int> > split( const std::list<int> &v, int delim) {
std::vector< std::list<int> > elems;
split( v, delim, elems);
return elems;
}
usage:
int main() {
std::list<int> v = { 1, 2, 3, 503, 5, 6, 502, 7, 510, 3, 500, 6, 7};
std::vector< std::list<int> > vl;
vl = split( v, 500);
int i = 0;
while( i < vl.size()) {
std::list<int>::const_iterator it = vl[ i].begin();
while( it != vl[ i].end())
std::cout << *it++;
std::cout << std::endl;
++i;
}
return 0;
}
http://ideone.com/VRpGft
prints:
123503
56502
7510
3500
67
First version
This version uses std::list::splice.
#include <iostream>
#include <list>
#include <vector>
std::vector< std::list<int> > & split( std::list<int> v,
int delim, std::vector< std::list<int> >& elems) {
auto it = v.begin();
while ( it != v.end()) {
std::list<int> l;
auto it3 = l.begin();
while ( it != v.end() && *it < delim) {
l.splice( it3, v, it);
it = v.begin();
}
if( it != v.end()) {
l.splice( it3, v, it);
it = v.begin();
}
elems.push_back( l);
}
return elems;
}
std::vector< std::list<int> > split( const std::list<int> &v, int delim) {
std::vector< std::list<int> > elems;
split( v, delim, elems);
return elems;
}
usage:
int main() {
std::list<int> v = { 1, 2, 3, 503, 5, 6, 502, 7, 510, 3, 500, 5, 9};
std::vector< std::list<int> > vl;
vl = split( v, 500);
int i = 0;
while( i < vl.size()) {
std::list<int>::const_iterator it = vl[ i].begin();
while( it != vl[ i].end())
std::cout << *it++;
++i;
}
return 0;
}
prints:
123503565027510350059
http://ideone.com/1xMehy
Second version
This is simplified version that doesn't use std::list::splice function. This function puts elements before the iterator so the loop would have to be slightly changed.
#include <iostream>
#include <list>
#include <vector>
std::vector< std::list<int> > & split( const std::list<int> & v,
int delim, std::vector< std::list<int> >& elems) {
std::list<int>::const_iterator it = v.begin();
while ( it != v.end()) {
std::list<int> l;
while ( it != v.end() && *it < delim) {
l.push_back( *it++);
}
if( it != v.end()) l.push_back( *it++);
elems.push_back( l);
}
return elems;
}
std::vector< std::list<int> > split( const std::list<int> &v, int delim) {
std::vector< std::list<int> > elems;
split( v, delim, elems);
return elems;
}
usage:
int main() {
std::list<int> v = { 1, 2, 3, 503, 5, 6, 502, 7, 510, 3, 500, 5, 9};
std::vector< std::list<int> > vl;
vl = split( v, 500);
int i = 0;
while( i < vl.size()) {
std::list<int>::const_iterator it = vl[ i].begin();
while( it != vl[ i].end())
std::cout << *it++;
++i;
}
return 0;
}
prints:
123503565027510350059
http://ideone.com/MBmlLE
Try the following
#include <vector>
#include <list>
#include <algorithm>
#include <functional>
//...
auto first = YourList.begin();
while ( first != YourList.end() )
{
auto last = std::find_if( first, YourList.end(), std::bind2nd( std::greater<int>(), 500 ) );
if ( last != YourList.end() ) ++last;
YourVector.push_back( std::list<int>( first, last ) );
first = last;
}
Loop through the numbers and get the distance between the locations where you need to split the list.
Use the splice function in List for each split location:
lst.splice( newLst.begin(), newLst, lst.begin(), lst.begin() + sliceLength);
http://www.cplusplus.com/reference/list/list/splice/
(Note that splice will destroy the original list)
I want to do something like this. Is there a stl algorithm that does this easily?
for each(auto aValue in aVector)
{
aMap[aValue] = 1;
}
If you have a vector of pairs, where the first item in the pair will be the key for the map, and the second item will be the value associated with that key, you can just copy the data to the map with an insert iterator:
std::vector<std::pair<std::string, int> > values {
{"Jerry", 1},
{ "Jim", 2},
{ "Bill", 3} };
std::map<std::string, int> mapped_values;
std::copy(values.begin(), values.end(),
std::inserter(mapped_values, mapped_values.begin()));
or, you could initialize the map from the vector:
std::map<std::string, int> m2((values.begin()), values.end());
Maybe like this:
std::vector<T> v; // populate this
std::map<T, int> m;
for (auto const & x : v) { m[x] = 1; }
You might std::transform the std::vector into a std::map
std::vector<std::string> v{"I", "want", "to", "do", "something", "like", "this"};
std::map<std::string, int> m;
std::transform(v.begin(), v.end(), std::inserter(m, m.end()),
[](const std::string &s) { return std::make_pair(s, 1); });
This creates std::pairs from the vector's elements, which in turn are inserted into the map.
Or, as suggested by #BenFulton, zipping two vectors into a map
std::vector<std::string> k{"I", "want", "to", "do", "something", "like", "this"};
std::vector<int> v{1, 2, 3, 4, 5, 6, 7};
std::map<std::string, int> m;
auto zip = [](const std::string &s, int i) { return std::make_pair(s, i); };
std::transform(k.begin(), k.end(), v.begin(), std::inserter(m, m.end()), zip);
Assuming items in vector are related in order, maybe this example can help :
#include <map>
#include <vector>
#include <string>
#include <iostream>
std::map<std::string, std::string> convert_to_map(const std::vector<std::string>& vec)
{
std::map<std::string, std::string> mp;
std::pair<std::string, std::string> par;
for(unsigned int i=0; i<vec.size(); i++)
{
if(i == 0 || i%2 == 0)
{
par.first = vec.at(i);
par.second = std::string();
if(i == (vec.size()-1))
{
mp.insert(par);
}
}
else
{
par.second = vec.at(i);
mp.insert(par);
}
}
return mp;
}
int main(int argc, char** argv)
{
std::vector<std::string> vec;
vec.push_back("customer_id");
vec.push_back("1");
vec.push_back("shop_id");
vec.push_back("2");
vec.push_back("state_id");
vec.push_back("3");
vec.push_back("city_id");
// convert vector to map
std::map<std::string, std::string> mp = convert_to_map(vec);
// print content:
for (auto it = mp.cbegin(); it != mp.cend(); ++it)
std::cout << " [" << (*it).first << ':' << (*it).second << ']';
std::cout << std::endl;
return 0;
}
A very generic approach to this, since you didn't specify any types, can be done as follows:
template<class Iterator, class KeySelectorFunc,
class Value = std::decay_t<decltype(*std::declval<Iterator>())>,
class Key = std::decay_t<decltype(std::declval<KeySelectorFunc>()(std::declval<Value>()))>>
std::map<Key, Value> toMap(Iterator begin, Iterator end, KeySelectorFunc selector) {
std::map<Key, Value> map;
std::transform(begin, end, std::inserter(map, map.end()), [selector](const Value& value) mutable {
return std::make_pair(selector(value), value);
});
return map;
}
Usage:
struct TestStruct {
int id;
std::string s;
};
std::vector<TestStruct> testStruct = {
TestStruct{1, "Hello"},
TestStruct{2, "Hello"},
TestStruct{3, "Hello"}
};
std::map<int, TestStruct> map = toMap(testStruct.begin(), testStruct.end(),
[](const TestStruct& t) {
return t.id;
}
);
for (const auto& pair : map) {
std::cout << pair.first << ' ' << pair.second.id << ' ' << pair.second.s << '\n';
}
// yields:
// 1 1 Hello
// 2 2 Hello
// 3 3 Hello
The parameter selector function is used to select what to use for the key in the std::map.
Try this:
for (auto it = vector.begin(); it != vector.end(); it++) {
aMap[aLabel] = it;
//Change aLabel here if you need to
//Or you could aMap[it] = 1 depending on what you really want.
}
I assume this is what you are trying to do.
If you want to update the value of aLabel, you could change it in the loop. Also, I look back at the original question, and it was unclear what they wanted so I added another version.
Yet another way:
#include <map>
#include <vector>
#include <boost/iterator/transform_iterator.hpp>
int main() {
using T = double;
std::vector<T> v;
auto f = [](T value) { return std::make_pair(value, 1); };
std::map<T, int> m(boost::make_transform_iterator(v.begin(), f),
boost::make_transform_iterator(v.end(), f));
}
But I don't think it beats range-for loop here in terms of readability and execution speed.
For converting values of a array or a vector directly to map we can do like.
map<int, int> mp;
for (int i = 0; i < m; i++)
mp[a[i]]++;
where a[i] is that array we have with us.
Is there a simple or standard way to have a multimap iterator which iterate across unique keys in a multimap?
i.e. for a set that looks like: {1, "a"}, {1, "lemon"}, {2, "peacock"}, {3, "angel"}
an iterator which would start at {1, "a"} then incrementing would point to {2, "peacock"} and then incrementing again would point to {3, "angel"}?
You can use upper_bound to increment the iterator position instead of ++:
#include <map>
#include <string>
#include <iostream>
using namespace std;
int main()
{
multimap<int,string> mm;
mm.insert(make_pair(1, "a"));
mm.insert(make_pair(1, "lemon"));
mm.insert(make_pair(2, "peacock"));
mm.insert(make_pair(3, "angel"));
for( auto it = mm.begin(), end = mm.end();
it != end;
it = mm.upper_bound(it->first)
)
cout << it->first << ' ' << it->second << endl;
return 0;
}
This results in:
1 a
2 peacock
3 angel
Using upper_bound would result in an easy-to-read loop but each call will perform a binary tree search, resulting in an O(n log n) instead of O(n) traversal. If the difference in efficiency matters, you can structure your traversal like this:
typedef std::multimap<std::string, int> MapType;
MapType container;
for (MapType::iterator it = container.begin(); it != container.end(); ) {
std::string key = it->first;
doSomething(key);
// Advance to next non-duplicate entry.
do {
++it;
} while (it != container.end() && key == it->first);
}
As noted in the selected answer, repeated use of multimap::upper_bound leads to an O(n log n) traversal of the map. Using the external upper_bound function gives you O(n). However, you need to ensure you only compare the key of the map:
std::multimap<int, std::string> myMap = ... ;
const auto compareFirst = [](const std::pair<const int, std::string>& lhs, const std::pair<const int, std::string>& rhs) {
return lhs.first < rhs.first;
};
for(auto it = myMap.begin(); it != myMap.end(); it = std::upper_bound(it, myMap.end(), *it, compareFirst)) {
// Do stuff...
}
The underlying approach is essentially the same as user3701170's solution - i.e linear search - but we put the increment step in the for statement proper, not the loop's body. Aside from putting the increment where it "usually" lives, this also means any continue statements in the loop will behave as expected.
Runnable example
This is a slight improvement over https://stackoverflow.com/a/24212648/895245 with a runnable unit test:
#include <cassert>
#include <map>
#include <vector>
int main() {
// For testing.
auto m = std::multimap<int, int>{
{1, 2},
{1, 3},
{2, 4}
};
std::vector<int> out;
// The algorithm.
auto it = m.begin();
auto end = m.end();
while (it != end) {
auto key = it->first;
// Do what you want to do with the keys.
out.push_back(key);
do {
if (++it == end)
break;
} while (it->first == key);
}
// Assert it worked.
assert(out == std::vector<int>({1, 2}));
}
if you have to pass over all unique keys quickly then you can use std::map instead;
typedef std::map< KeyType, std::list< ValueType > > MapKeyToMultiValue;
Insertion would be more difficult, However you can iterate over all keys without having to bother with duplicate entries. Insertion would look as follows:
void insert_m(MapKeyToMultiValue &map, const KeyType key, const ValueType value )
{
auto it = map.find( key );
if (it == map.end())
{
std::list<ValueType> empty;
std::pair< MapKeyToMultiValue::iterator, bool > ret =
map.insert( MapKeyToMultiValue::value_type( key, empty ) );
it = ret.first;
}
it->second.push_back( value );
}
or you can make that very templated:
template<typename KeyType, typename ValueType,
typename MapType = std::map< KeyType, std::list< ValueType > > >
void insert_multi( MapType &map, const KeyType key, const ValueType value )
{
auto it = map.find( key );
if (it == map.end())
{
std::list<ValueType> empty;
std::pair< typename MapType::iterator, bool > ret =
map.insert( typename MapType::value_type( key, empty ) );
it = ret.first;
}
it->second.push_back( value );
}
The full test program looks as follows:
#include <map>
#include <list>
#include <string>
#include <stdio.h>
typedef std::string KeyType;
typedef int ValueType;
typedef std::map< KeyType, std::list< ValueType > > MapKeyToMultiValue;
void insert_m(MapKeyToMultiValue &map, const KeyType key, const ValueType value )
{
auto it = map.find( key );
if (it == map.end())
{
std::list<ValueType> empty;
std::pair< MapKeyToMultiValue::iterator, bool > ret =
map.insert( MapKeyToMultiValue::value_type( key, empty ) );
it = ret.first;
}
it->second.push_back( value );
}
template<typename KeyType, typename ValueType,
typename MapType = std::map< KeyType, std::list< ValueType > > >
void insert_multi( MapType &map, const KeyType key, const ValueType value )
{
auto it = map.find( key );
if (it == map.end())
{
std::list<ValueType> empty;
std::pair< typename MapType::iterator, bool > ret =
map.insert( typename MapType::value_type( key, empty ) );
it = ret.first;
}
it->second.push_back( value );
}
int main()
{
MapKeyToMultiValue map;
insert_m(map, std::string("aaa"), 1 );
insert_m(map, std::string("aaa"), 2 );
insert_m(map, std::string("bb"), 3 );
insert_m(map, std::string("cc"), 4 );
insert_multi(map, std::string("ddd"), 1 );
insert_multi(map, std::string("ddd"), 2 );
insert_multi(map, std::string("ee"), 3 );
insert_multi(map, std::string("ff"), 4 );
for(auto i = map.begin(); i != map.end(); ++i)
{
printf("%s\n", i->first.c_str() );
}
return 0;
}
Try equal_range:
http://en.cppreference.com/w/cpp/container/multimap/equal_range
That must be an exact match.
Given that I have two std::maps, say:
map<int, double> A;
map<int, double> B;
I'd like to get the intersection of the two maps, something of the form:
map<int, pair<double,double> > C;
Where the keys are the values in both A and B and the value is a pair of the values from A and B respectively.
Is there a clean way using the standard-library?
#include <map>
#include <utility>
template <typename KeyType, typename LeftValue, typename RightValue>
std::map<KeyType, std::pair<LeftValue, RightValue>>
IntersectMaps(const std::map<KeyType, LeftValue>& left,
const std::map<KeyType, RightValue>& right) {
std::map<KeyType, std::pair<LeftValue, RightValue>> result;
typename std::map<KeyType, LeftValue>::const_iterator il = left.begin();
typename std::map<KeyType, RightValue>::const_iterator ir = right.begin();
while (il != left.end() && ir != right.end()) {
if (il->first < ir->first)
++il;
else if (ir->first < il->first)
++ir;
else {
result.insert(std::make_pair(il->first, std::make_pair(il->second, ir->second)));
++il;
++ir;
}
}
return result;
}
I haven't tested this, or even compiled it... but it should be O(n). Because it's templated it should work with any two maps that share the same key type.
I don't think there is a pure STL way of implementing what you want. Manual implementation should not be too complicated.
Note that std::set_intersection is not a solution. The main reason being that it compares the dereferenced iterators and then copies one of the elements to the output iterator.
While comparison of the full dereferenced iterator includes the associated value (which I understand you do not want to consider as part of the key), can be solved by providing a comparison functor that would only test the key (std::pair<const Key, Value>::first), the problem of the algorithm copying only one of the two values and not composing the solution cannot be tackled externally.
EDIT: A simple linear time implementation of the function:
Note, as #Mark Ransom comments, that this solution adds an extra requirement: the keys must be equality comparable. That is not an issue with his solution here, or similarly in the answer by #Matthiew M here. It would be shameful to modify this algorithm with that fix :)
Another great advantage of #Mark's implementation is that it can compose from maps that store different value types as long as the keys are the same (which seems like an obvious requirement). I wish I would upvote more than once there..
template <typename Key, typename Value>
std::map<Key,std::pair<Value,Value> >
merge_maps( std::map<Key,Value> const & lhs, std::map<Key,Value> const & rhs )
{
typedef typename std::map<Key,Value>::const_iterator input_iterator;
std::map<Key, std::pair<Value,Value> > result;
for ( input_iterator it1 = lhs.begin(), it2 = rhs.begin(),
end1 = lhs.end(), end2 = rhs.end();
it1 != end1 && it2 != end2; )
{
if ( it1->first == it2->first )
{
result[it1->first] = std::make_pair( it1->second, it2->second );
++it1; ++it2;
}
else
{
if ( it1->first < it2->first )
++it1;
else
++it2;
}
}
return result;
}
#include <map>
using namespace std;
typedef int KeyType;
typedef double ValueType;
typedef map< KeyType, ValueType > MyMap;
typedef MyMap::iterator MyMapIter;
typedef MyMap::const_iterator MyMapConstIter;
typedef pair< ValueType, ValueType > ValueTypePair;
typedef map< KeyType, ValueTypePair > MyMapIntersection;
int main() {
MyMap A;
MyMap B;
MyMapIntersection C;
// fill up A, B
for( MyMapConstIter cit = A.begin(); cit != A.end(); ++cit ) {
const KeyType x = cit->first;
MyMapConstIter found = B.find( x );
if( found != B.end() ) {
ValueTypePair valuePair =
ValueTypePair( cit->second, found->second );
C.insert( pair< KeyType, ValueTypePair>( x, valuePair ) );
}
}
}
A (better) solution based on the fact that the maps are sorted. (Shame on me that I overlooked it.) Thanks to David RodrÃguez - dribeas for the suggestion.
#include <map>
using namespace std;
typedef int KeyType;
typedef double ValueType;
typedef map< KeyType, ValueType > MyMap;
typedef MyMap::iterator MyMapIter;
typedef MyMap::const_iterator MyMapConstIter;
typedef pair< ValueType, ValueType > ValueTypePair;
typedef map< KeyType, ValueTypePair > MyMapIntersection;
void constructInsert( MyMapIntersection & c, MyMapConstIter const & acit,
MyMapConstIter const & bcit ) {
ValueTypePair valuePair = ValueTypePair( acit->second, bcit->second );
c.insert( pair< KeyType, ValueTypePair>( acit->first, valuePair ) );
}
int main() {
MyMap A;
MyMap B;
MyMapIntersection C;
// fill up A, B
MyMapConstIter acit, bcit;
for( acit = A.begin(), bcit = B.begin();
(acit != A.end()) && (bcit != B.end()); /* Inside loop */ ) {
const KeyType aKey = acit->first;
const KeyType bKey = bcit->first;
if( aKey < bKey ) {
++acit;
}
else if( aKey == bKey ) {
constructInsert( C, acit, bcit );
++acit;
++bcit;
}
else {
++bcit;
}
}
}
Okay, let's get ready to get your hands dirty :)
I'll be using std::mismatch and std::transform
First of all, some types:
typedef std::map<int, double> input_map;
typedef input_map::const_reference const_reference;
typedef input_map::const_iterator const_iterator;
typedef std::pair<const_iterator,const_iterator> const_pair;
typedef std::map<int, std::pair<double,double> > result_map;
Then predicates
bool less(const_reference lhs, const_reference rhs)
{
return lhs.first < rhs.first;
}
result_map::value_type pack(const_reference lhs, const_reference rhs)
{
assert(lhs.first == rhs.first);
return std::make_pair(lhs.first, std::make_pair(lhs.second, rhs.second));
}
Now main:
result_map func(input_map const& m1, input_map const& m2)
{
if (m1.empty() || m2.empty()) { return result_map(); }
// mismatch unfortunately only checks one range
// god do I hate those algorithms sometimes...
if (*(--m1.end()) < *(--m2.end()) { return func(m2, m1); }
const_pair current = std::make_pair(m1.begin(), m2.begin()),
end = std::make_pair(m1.end(), m2.end());
result_map result;
// Infamous middle loop, the check is middle-way in the loop
while(true)
{
const_pair next = std::mismatch(p.first, end.first, p.second, less);
std::transform(current.first, next.first, current.second,
std::inserter(result, result.begin()), pack);
// If any of the iterators reached the end, then the loop will stop
if (next.first == end.first || next.second == end.second) { break; }
// Advance the lesser "next"
if (less(*next.first, *next.second)) { ++next.first; }
else { ++next.second; }
current = next;
}
return result;
}
I find this solution quite elegant... notwithstanding the awkard setup part since we need to ensure that the first range ends up quicker than the second because of mismatch...
Notice that the advance is really stupid, we could loop specifically here until we had *next.first.key == *next.second.key but it would complicate the loop.
I really don't find this better than a handcrafted loop though... consider:
result_map func2(input_map const& lhs, input_map const& rhs)
{
result_map result;
for (const_iterator lit = lhs.begin(), lend = lhs.end(),
rit = rhs.begin(), rend = rhs.end();
lit != lend && rit != rend;)
{
if (lit->first < rit->first) { ++lit; }
else if (rit->first < lit->first) { ++rit; }
else
{
result[lit->first] = std::make_pair(lit->second, rit->second);
++lit, ++rit;
}
}
return result;
}
It's much more compact, probably more efficient... sometimes the functions you're looking are not general enough to be in the STL :)
EDIT: Since I was pretty sure there was a better STL-like solution to this, I figured one out. It's different enough that I'm posting it as a separate answer.
There are a few tricks to this. Firstly, you'd like to use set_intersection, but you have two maps. The solution is a custom comparator and the std::transform algorithm. Someone more familiar with the standard library than me can probably optimize this, but it works. Note that boost::bind would allow you to cut down on the silly helper functions that make this work.
#include <algorithm>
#include <map>
#include <set>
bool myLess(const std::map<int,double>::value_type &v1,
const std::map<int,double>::value_type &v2) {
return v1.first < v2.first;
}
int getKey(const std::map<int,double>::value_type &v) {
return v.first;
}
struct functor {
std::map<int,double> &m1,&m2;
functor(std::map<int,double> &im1, std::map<int,double> &im2) : m1(im1), m2(im2) {}
std::pair<int,std::pair<double,double> > operator() (int x) {
return std::make_pair(x, std::make_pair(m1[x],m2[x]));
}
};
int main() {
std::map<int,double> m1, m2;
m1[0]=0;m1[1]=1; m1[2]=2; m1[3]=3;
m2[1]=11;m2[2]=12;m2[3]=13;m2[4]=14;
std::set<int> keys1,keys2,keys;
//Extract the keys from each map with a transform
std::transform(m1.begin(),m1.end(),std::inserter(keys1,keys1.begin()),getKey);
std::transform(m2.begin(),m2.end(),std::inserter(keys2,keys2.begin()),getKey);
//set_intersection to get the common keys
std::set_intersection(keys1.begin(),keys1.end(),keys2.begin(),keys2.end(),
std::inserter(keys,keys.begin()));
std::map<int, std::pair<double,double> > result;
functor f(m1,m2); //stash our maps into the functor for later use
//transform from the key list to the double-map
std::transform(keys.begin(),keys.end(),std::inserter(result,result.begin()),f);
return 0;
}
Like much of C++, the final use of everything is fairly slick (everything in main()), but the setup is more verbose than we would really like.
The following is a simplification of my previous answer, mostly taking advantage of the fact that set_intersection CAN be used with maps as input, but only if you make the output a set of std::pairs. The result also cuts down intermediates to a single "common keys" list.
#include <algorithm>
#include <map>
#include <set>
struct cK { //This function object does double duty, the two argument version is for
//the set_intersection, the one argument version is for the transform
std::map<int,double> &m1,&m2;
cK(std::map<int,double> &im1, std::map<int,double> &im2) : m1(im1), m2(im2)
std::pair<int,std::pair<double,double> > operator() (std::pair<int,double> v
return std::make_pair(v.first, std::make_pair(m1[v.first],m2[v.first]));
}
bool operator() (std::pair<int,double> v1, std::pair<int,double> v2) {
return v1.first < v2.first;
}
};
int main() {
std::map<int,double> m1, m2;
m1[0]=0;m1[1]=1; m1[2]=2; m1[3]=3;
m2[1]=11;m2[2]=12;m2[3]=13;m2[4]=14;
// Get the subset of map1 that has elements in map2
std::set<std::pair<int,double> > sIntersection;
cK compareKeys(m1,m2);
std::set_intersection(m1.begin(),m1.end(),m2.begin(),m2.end(),
std::inserter(sIntersection,sIntersection.begin()),compareKeys);
// Use a custom transform to produce an output set
std::map<int, std::pair<double,double> > result;
std::transform(sIntersection.begin(),sIntersection.end(),
std::inserter(result,result.begin()), compareKeys);
return 0;
}
Almost a year after... but nevertheless :)
This one is for a set container, but you can easily change it to use a map:
template <class InputIterator, class OutputIterator>
OutputIterator intersect(InputIterator lf, InputIterator ll,
InputIterator rf, InputIterator rl,
OutputIterator result)
{
while(lf != ll && rf != rl)
{
if(*lf < *rf)
++lf;
else if(*lf > *rf)
++rf;
else
{
*result = *lf;
++lf;
++rf;
}
}
return result;
}
Usage:
intersect(set1.begin(), set1.end(),
set2.begin(), set2.end(),
inserter(output_container, output_container.begin()));
set1 and set2 are both set containers whilst output_container can be set, list, array etc..
inserter generates an insert iterator
template<typename K, typename V>
std::map<K, V> UnionMaps(const std::map<K, V> & left, const std::map<K, V> & right)
{
std::map<K, V > result;
typename std::map<K, V>::const_iterator il = left.begin();
typename std::map<K, V>::const_iterator ir = right.begin();
while (il != left.end() && ir != right.end())
{
if ((il->first < ir->first)){
result.insert(make_pair(il->first, il->second));
++il;
}else if ((ir->first < il->first)){
result.insert(make_pair(ir->first, ir->second));
++ir;
}else{
result.insert(make_pair(il->first, il->second+ir->second));//add
++il;
++ir;
}
}
while (il != left.end() ){
result.insert(make_pair(il->first, il->second));
il++;
}
while (ir != right.end() ){
result.insert(make_pair(ir->first, ir->second));
ir++;
}
return result;
}