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.
Related
Code worked on:
#include <algorithm>
#include <list>
#include <vector>
class key_value_sequences {
public:
int size(int key);
int * data(int key);
void insert(int key, int value);
private:
list< pair<int, vector<int> > > myList;
}; // class key_value_sequences
#endif
void key_value_sequences::insert(int key, int value){
list< pair<int, vector<int> > >::iterator it;
for(it = myList.begin(); it != myList.end(); ++it){
if (it->first == key){
it->second.push_back(value);
return;
}
}
vector<int> v;
v.push_back(value);
myList.push_back(make_pair(key, v));
return;
};
int * key_value_sequences::data(int key){
list< pair<int, vector<int> > >::iterator it;
for(it = myList.begin(); it != myList.end(); ++it){
if (it->first == key){
return (it->second);
}
}
vector<int> v;
return v;
};
int key_value_sequences::size(int key){
list< pair<int, vector<int> > >::iterator it;
for(it = myList.begin(); it != myList.end(); ++it){
if (it->first == key){
return it->second.size();
}
}
return -1;
};
I am getting errors for template arguments, and can't figure out why. It looks like this line
std::list< pair<int, vector<int> > > myList;
is throwing errors
error: template argument 1 is invalid
std::list< pair<int, vector<int> > > myList;
^
error: template argument 2 is invalid
error: expected unqualified-id before ‘>’ token
std::list< pair<int, vector<int> > > myList;
^
I can't figure out why.
I'm also stuck with errors
/usr/include/c++/5/bits/stl_algobase.h:840:58: error: no type named ‘value_type’ in ‘struct std::iterator_traits<std::vector<int> >’
typedef typename iterator_traits<_II2>::value_type _ValueType2;
^
/usr/include/c++/5/bits/stl_algobase.h:845:9: error: no type named ‘value_type’ in ‘struct std::iterator_traits<std::vector<int> >’
&& __are_same<_ValueType1, _ValueType2>::__value);
^
The instantiation of the iterator is:
list<pair<int, vector<int>>>::iterator it;
Edit trying out vector hashtable:
class key_value_sequences {
public:
int size(int key);
int* data(int key);
void insert(int key, int value);
private:
vector<list<pair<int,int>>> hash_table;
list<pair<int, vector<int>>>::iterator it;
int hash(int value)
{
return abs(value%static_cast<int>(hash_table.size()));
}
}; // class key_value_sequences
#endif // A3_HPP
void key_value_sequences::insert(int key, int value){
list<pair<int,int>> &collisionlist = hash_table[hash(key)];
for (std::pair<int,int> &test: collisionlist)
{
if (key == test.first)
{
test.second = value; // update existing
return;
}
}
collisionlist.push_back(pair<int,int>(key, value));
};
int* key_value_sequences::data(int key){
for(it = hash_table.begin(); it != hash_table.end(); ++it){
if (it->first == key){
return &(it->second[0]);
}
}
return nullptr;
};
int key_value_sequences::size(int key){
for(it = hash_table.begin(); it != hash_table.end(); ++it){
if (it->first == key){
return it->second.size();
}
}
return -1;
};
Tried adding as much detail as I could in the comments, but this successfully passed all tests on my end. While I mentioned I reduced the original copy constructor from O(keys.length + vals.size) to just O(vals.size)- I lied.
resize() is linear in the length of the vector- so it's best to leave that alone.
#include <iostream>
#include <vector>
#include <list>
using namespace std;
class key_value_sequences{
public:
int size(int key);
int * data(int key);
void insert(int key, int value);
key_value_sequences(){}; //ctor
key_value_sequences(const key_value_sequences &_rhs); //[heli]coptor
~key_value_sequences(){}; //dtor
private:
vector <vector<int> *> keys;
list <vector<int> > vals;
};
key_value_sequences::key_value_sequences(const key_value_sequences &_rhs){
keys.resize(_rhs.keys.size()); //resize new kvs key vector to old size
auto it = _rhs.vals.begin();
while (it != _rhs.vals.end()){
vals.push_back(*it); //push back value vector to list
keys[(*it)[0]] = &vals.back(); //use the prepended key value of value vector
++it; // to reestablish ref in key vector
}
}
void key_value_sequences::insert(int key, int value){
if (key > -1 && key + 1 > keys.size()){ //if key index is valid & > key vector size
keys.resize(key+1, new vector<int>); //resize the vector to make room
vector<int> v;
vals.push_back(v); //push back new value vector to list
vals.back().push_back(key); //create key # front of list for the [heli]coptor
vals.back().push_back(value); //push back initial value
keys[key] = &vals.back(); //update reference in key vector
}
else if (key > -1){
keys[key]->push_back(value); //index already exists, push back value to value vector
}
return;
}
int * key_value_sequences::data(int key){
if (key + 1 > keys.size() || key < 0){
return nullptr;
}
else{
return &keys[key]->at(1); //if index is valid: return second element of value vector
} //in order to account for the prepended key
}
int key_value_sequences::size(int key){
if (key < 0 || keys[key]->empty() || key + 1 > keys.size()){
return -1;
}
else{
return keys[key]->size() - 1; //if index is valid: return size - 1 to account for key
}
}
To answer the title of your question, you can use the push_back methods of std::list and std::vector to put items into those containers.
The items will stay in the container until the container is deleted, the items are deleted or your program stops executing.
To find items in your containers, you can search using a loop. The std::list and std::vector both support iterators for iterating through the container. Items in a std::vector can be retrieved using array syntax.
It sounds to me like you need a multimap. A map is a container that allows you to insert key / value pairs, where keys can be used to look up values. A multimap allows you to have multiple values associated with a single key.
For example:
std::multimap<int, int> myMap;
myMap.insert( std::make_pair( 0, 8 ) );
myMap.insert( std::make_pair( 0, 5 ) );
myMap.insert( std::make_pair( 0, 7 ) );
myMap.insert( std::make_pair( 1, 15 ) );
// equal_range() returns a pair of iterators pointing to the first item
// in the list for the specified key, and one past the final item containing
// the key.
auto searchResultIteratorPair = myMap.equal_range( 0 );
// Print out the values found
for( auto it = searchResultIteratorPair.first; it != searchResultIteratorPair.second; it++ )
{
std::cout << "Value: " << it->second << std::endl;
}
If my assumption was wrong and you really did want to use a list / vector, then you would need to create them as a list / vector of pairs. Then to find items you would iterate the entire list and check each pair to see if it matched your criteria.
For example:
std::list< std::pair<int, int> > myList;
myList.push_back( std::make_pair( 0, 8 ) );
myList.push_back( std::make_pair( 0, 5 ) );
myList.push_back( std::make_pair( 0, 7 ) );
myList.push_back( std::make_pair( 1, 15 ) );
int searchValue = 0;
for( auto it = myList.begin(); it != myList.end(); it++ )
{
if( it->first != searchValue )
continue;
std::cout << "Value: " << it->second << std::endl;
}
I want to use std::find function along with a predicate (not sure if I use the correct word). Here is the code
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class foo {
public:
typedef pair< int, vector<int> > way;
typedef pair< int, int > index;
typedef pair< index, vector<way> > entry;
vector< entry > table;
void bar()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
way w = make_pair( 1, v1 );
vector<way> v2;
v2.push_back(w);
index id = make_pair( 10, 20 );
entry en = make_pair( id, v2 );
table.push_back( en );
}
void insert()
{
index new_id = make_pair( 10, 20 );
if ( find(table.begin(), table.end(), new_id) != table.end() ) {
// index matched in the table
// then I will push back a new pair (way)
// to the second part of the entry
}
}
};
int main()
{
foo f;
f.bar();
f.insert();
return 0;
}
As you can see, find() should search the table based on the first element in each entry. Right now, it says that == is not overloaded to compare a pair.
You want std::find_if:
...
if(find_if(table.begin(), table.end(), [&new_id](const entry &arg) {
return arg.first == new_id; }) != ...)
EDIT: If you don't have C++11 (and therefore no lambdas), you have to create a custom functor (function or function object) to do the comparison of entry::first with the searched index:
struct index_equal : std::unary_function<entry,bool>
{
index_equal(const index &idx) : idx_(idx) {}
bool operator()(const entry &arg) const { return arg.first == idx_; }
const index &idx_;
};
...
if(find_if(table.begin(), table.end(), index_equal(new_id)) != ...)
EDIT: Since an index is just a pair of ints, you may also just capture it by value than const reference, to keep the code clearer and more concise, but it doesn't really matter either.
In C++11, you can use also std::any_of
if (std::any_of(table.cbegin(), table.cend(),
[&new_id](const entry &arg) { return arg.first == new_id; }))
What basically I am doing is the set cover problem, removing duplicate from the vectors that has the same numbers. An example:
I have the following vectors of vectors after sorting:
{{1,2,3,4,5},{2,3,7,8},{10,11,12}}
NOW, I would like to remove the occurrence from 2nd vector which is 2,3 and sort again ...
{{1,2,3,4,5},{10,11,12},{7,8}}
I have implemented some code to sort the vectors of vectors but i have problem removing the occurrences from the vector that has less size?
The sort function:
sort(ROWS.begin(),ROWS.end(),VectorsSort());
Thanks for the help.
Pick this apart and take what you need:
#include <algorithm>
#include <vector>
struct binary_search_pred
{
typedef bool result_type;
explicit binary_search_pred(std::vector<int> const& v) : v_(&v) { }
bool operator ()(int const& i) const
{
return std::binary_search(v_->begin(), v_->end(), i);
}
private:
std::vector<int> const* v_;
};
struct size_comparison_desc
{
typedef bool result_type;
typedef std::vector<int> const& arg_t;
bool operator ()(arg_t a, arg_t b) const
{
return b.size() < a.size();
}
};
void set_cover(std::vector<std::vector<int> >& vec)
{
typedef std::vector<std::vector<int> > vec_t;
typedef vec_t::iterator iter_t;
typedef vec_t::const_iterator citer_t;
if (vec.empty() || vec.size() == 1)
return;
for (iter_t v = vec.begin() + 1, v_end = vec.end(); v != v_end; ++v)
for (citer_t p = vec.begin(); p != v; ++p)
v->erase(
std::remove_if(v->begin(), v->end(), binary_search_pred(*p)),
v->end()
);
std::sort(vec.begin(), vec.end(), size_comparison_desc());
}
(Note that set_cover requires that the contained std::vector<int>s given must already be sorted.)
EDIT #1:
As requested in now-deleted comments, a version oriented around std::map<int, std::vector<int>> instead of std::vector<std::vector<int>> (use binary_search_pred from the original code):
#include <algorithm>
#include <map>
#include <vector>
void set_cover(std::map<int, std::vector<int> >& m)
{
typedef std::map<int, std::vector<int> > map_t;
typedef map_t::iterator iter_t;
typedef map_t::const_iterator citer_t;
if (m.empty() || m.size() == 1)
return;
for (iter_t v = ++m.begin(), v_end = m.end(); v != v_end; ++v)
for (citer_t p = m.begin(); p != v; ++p)
v->second.erase(
std::remove_if(
v->second.begin(),
v->second.end(),
binary_search_pred(p->second)
),
v->second.end()
);
}
Note that the map here will always be sorted by its key, never by the contained vector's size (which is what you appear to want). Maybe you want a std::vector<std::pair<int, std::vector<int>>> instead?
EDIT #2:
As requested in now-deleted comments, a version oriented around std::vector<std::pair<int, std::vector<int>>> instead of std::map<int, std::vector<int>> (use binary_search_pred from the original code):
#include <algorithm>
#include <utility>
#include <vector>
struct size_comparison_desc
{
typedef bool result_type;
typedef std::pair<int, std::vector<int> > const& arg_t;
bool operator ()(arg_t a, arg_t b) const
{
return b.second.size() < a.second.size();
}
};
void set_cover(std::vector<std::pair<int, std::vector<int> > >& vec)
{
typedef std::vector<std::pair<int, std::vector<int> > > vec_t;
typedef vec_t::iterator iter_t;
typedef vec_t::const_iterator citer_t;
if (vec.empty() || vec.size() == 1)
return;
for (iter_t v = vec.begin() + 1, v_end = vec.end(); v != v_end; ++v)
for (citer_t p = vec.begin(); p != v; ++p)
v->second.erase(
std::remove_if(
v->second.begin(),
v->second.end(),
binary_search_pred(p->second)
),
v->second.end()
);
std::sort(vec.begin(), vec.end(), size_comparison_desc());
}
You shouldn't need to define a vector sorting predicate. Vector already defines an operator< which is based on std::lexicographical_compare.
Is this what you mean?
std::for_each(rows.begin(), rows.end(), [](std::vector<int>& row) {
row.erase(std::remove_if(row.begin(), row.end(), [](int number) -> bool {
return (number == 2) || (number == 3);
}), row.end());
});
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;
}
I'm aware that map is not prepared to be sorted. It's heavily optimized for fast and random key access and actually doesn't support std::sort.
My current problem is that I've a full map<std::string,int> which I'm not going to use anymore. I just need to extract 10 pairs in value(int) order and destroy it.
The best thing, if it was possible, would be to sort it in place and then iterate it 10 times, but that apparently is not a solution.
I'm trying different solutions as going through a multimap<int,string> (to allow duplicate keys), but I'd like to know if there is a more elegant solution, using stl algorithms as much as posible.
EDIT:
I'm using a map because for the 99% of the time, I need it as a map: fast key lookups to increase values. Just need a good way of later extracting in value order when I don't need the map anymore.
Current approach whould be:
std::copy the map(std::string,int) to a vector(pair(std::string,int))
sort the vector
get the first 10 values
destroy vector and map
Maps are stored as a tree sorted in key order. You want the 10 smallest (or largest) integer values, and their keys, right?
In that case, iterate the map and put all the key-value pairs in a vector of pairs (std::vector<std::pair<std::string, int> >). I think you can just use the two-iterator-arg constructor of std::vector for this. Then use std::partial_sort on the vector. Specify a comparator to partial_sort, which compares pairs by just comparing the value int, ignoring the key string. Then you have the 10 pairs you want at the start of the vector, and the rest of the vector contains the remaining pairs in an unspecified order.
Code (untested):
typedef std::pair<std::string, int> mypair;
struct IntCmp {
bool operator()(const mypair &lhs, const mypair &rhs) {
return lhs.second < rhs.second;
}
};
void print10(const std::map<std::string,int> &mymap) {
std::vector<mypair> myvec(mymap.begin(), mymap.end());
assert(myvec.size() >= 10);
std::partial_sort(myvec.begin(), myvec.begin() + 10, myvec.end(), IntCmp());
for (int i = 0; i < 10; ++i) {
std::cout << i << ": " << myvec[i].first
<< "-> " << myvec[i].second << "\n";
}
}
Note that if there are several strings with the same value, either side of the limit of 10, then it's not specified which ones you get. You can control this by having your comparator look at the string too, in cases where the integers are equal.
For iterating by value you could use boost::multi_index. It will looks as follows:
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/hashed_index.hpp>
using namespace boost::multi_index;
struct X {
X( std::string val_str, int val_int ) : val_str(val_str), val_int(val_int) {};
std::string val_str;
int val_int;
};
typedef multi_index_container<
X,
indexed_by<
hashed_unique< member<X, std::string, &X::val_str> >,
ordered_non_unique< member<X, int, &X::val_int> >
>
> X_map;
void func()
{
X_map data;
data.insert( X("test", 1) );
// ...
// search by val_str
// complexity is equal to O(1) for hashed index (worst cast O(n) ),
// and O(log n) for ordered index
X_map::const_iterator it = data.find( "test" );
// ...
// iterate in order of val_int
size_t N = 0;
for ( X_map::nth_index<1>::type::const_iterator it = data.get<1>().begin(); N < 10 && it != data.get<1>().end(); ++it, ++N ) {
// copy elements somewhere
}
}
You could use any index for iteration ( val_str or val_int ).
May not be the most elegant way, but you can sort them via value in a set as:
#include <map>
#include <set>
#include <iostream>
#include <string>
using namespace std;
struct sortPairSecond
{
bool operator()(const pair<string, int> &lhs, const pair<string, int> &rhs)
{
return lhs.second < rhs.second;
}
};
int main (int argc, char *argv[])
{
cout << "Started...\n";
map<string, int> myMap;
myMap["One"] = 1;
myMap["Ten"] = 10;
myMap["Five"] = 5;
myMap["Zero"] = 0;
myMap["Eight"] = 8;
cout << "Map Order:\n---------------\n";
set<pair<string,int>, sortPairSecond > mySet;
for(map<string, int>::const_iterator it = myMap.begin(); it != myMap.end(); ++it)
{
cout << it->first << " = " << it->second << "\n";
mySet.insert(*it);
}
cout << "\nSet Order:\n--------------\n";
for(set<pair<string, int> >::const_iterator it = mySet.begin(); it != mySet.end(); ++it)
{
cout << it->first << " = " << it->second << "\n";
}
return 1;
}
If you iterate using the map iterator, you will get the items sorted on key as it internally uses balanced binary tree to store the values. So you could just extract the 10 values from it using the iterators. Is that what you want or you want to do something else? Please clarify.
EDIT:
Instead of using the vector and sorting, you can directly use set and pass the comparison function. Then you can extract the top 10 elements. This is my test code:
typedef std::pair<std::string, int> MyPair;
struct MyTestCompare
{
bool operator()(const MyPair& firstPair, const MyPair& secondPair) const
{
return firstPair.second < secondPair.second;
}
};
int main()
{
std::map<std::string, int> m;
m[std::string("1")] = 10;
m[std::string("2")] = 40;
m[std::string("3")] = 30;
m[std::string("4")] = 20;
std::set<MyPair,MyTestCompare> s;
std::map<std::string, int>::iterator iter = m.begin();
std::map<std::string, int>::iterator endIter = m.end();
for(; iter != endIter; ++iter)
{
s.insert(*iter);
}
}
Another possibility is to build a reverse map. For you that would be std::map<int, std::string>. Entries in the reverse map are sorted by their value.
The following is what I have in my tool box for such occasions:
template< typename TK, typename TV, class TP, class TA, typename T1, typename T2 >
inline void asserted_insert(std::map<TK,TV,TP,TA>& m, const T1& k, const T2& v)
{
typedef std::map<TK,TV,TP,TA> map_type;
typedef typename map_type::value_type value_type;
assert( m.insert(value_type(k,v)).second );
}
template< class TMap > struct reverse_map;
template< typename T1, typename T2 > struct reverse_map< std::map<T1,T2> > {
typedef std::map<T2,T1> result_t;
};
template< typename T1, typename T2, class TP1, class TA1, class TP2, class TA2 >
inline void build_reverse_map(const std::map<T1,T2,TP1,TA1>& map, std::map<T2,T1,TP2,TA2>& reverse_map)
{
typedef std::map<T1,T2,TP1,TA1> map_type;
for( typename map_type::const_iterator it=map.begin(),
end=map.end(); it!=end; ++it ) {
asserted_insert( reverse_map, it->second, it->first );
}
}
This code assumes that values are unique, too (and throws an assertion, if this is not the case). If this doesn't apply to your problem, you could easily change the code to use a multi map.