std::multimap getting two ranges - c++

I'm using a C++ std::multimap and I have to loop over two different keys. Is there an efficient way to do this other than creating two ranges and looping over those ranges seperately?
This is the way im doing it now:
std::pair<std::multimap<String, Object*>::iterator,std::multimap<String, Object*>::iterator> range;
std::pair<std::multimap<String, Object*>::iterator,std::multimap<String, Object*>::iterator> range2;
// get the range of String key
range = multimap.equal_range(key1);
range2 = multimap.equal_range(key2);
for (std::multimap<String, Object*>::iterator it = range.first; it != range.second; ++it)
{
...
}
for (std::multimap<String, Object*>::iterator it2 = range2.first; it2 != range2.second; ++it2)
{
...
}

The code you started with is the most straightforward.
If you'd really like to iterate over two ranges in the same loop, you can create a custom iterator that takes two iterator ranges, iterates over the first until it's done then switches to the second. This is probably more trouble than it's worth, as you'd need to implement all of the iterator members yourself.
Edit: I was overthinking this; it's easy just to modify the two loops into a single one.
for (std::multimap<String, Object*>::iterator it = range.first; it != range2.second; ++it)
{
if (it == range.second)
{
it = range2.first;
if (it == range2.second)
break;
}
...
}

Boost does this, of course. Using Boost.Range and its join function will get you what you want. See Boost Range Library: Traversing Two Ranges Sequentially for more details.

If you have access to C++-11 (Visual Studio 10+, gcc-4.5+) and are allowed to use it auto is a real gem:
// get the range of String key
auto range = multimap.equal_range(key1);
auto range2 = multimap.equal_range(key2);
for (auto it = range.first; it != range.second; ++it)
{
...
}
for (auto it2 = range2.first; it2 != range2.second; ++it2)
{
...
}
Anyway, I would just test the keys and only do the second loop if key2 != key1. Checking iterators each time in a loop has some cost.
A std::set_difference of the first range from the second might streamline the code.
Maybe std::set_union the two ranges and insert through back_inserter into a set so you only get one copy?
Some experiments may be in order. Don't forget to put your first guess in the mix. It might surprise you by being just fine in terms of speed. Unless the ranges are typically very long and/or the loop operation is expensive it may not be worth the headache of extra bookkeeping.

Related

How to reliably "clamp" an iterator?

I'm iterating through a vector as follows:
for(auto it = myVector.begin(); it != myVector.end(); ++it){
// Do some stuff
}
Inside the loop, based on some condition, I add a variable amount to the iterator so as to "jump forward" (but, importantly, never "move back"). What is the "right" way (or, at least, what is an effective/reliable way) to "clamp" the iterator such that I don't end up going beyond the end of the vector (ending up with a program crash or, worse yet, undefined behaviour)?
if (myVector.end() - it < variable_amount)
it += variable_amount;
else
break;
Using std::distance and std::advance would allow this to work with non-random access iterators, but that would be an inefficient algorithm for them, so I recommend not using them.
Algorithm for non-random-access iterators:
for (; variable_amount-- && it != myList.end(); ++it);
You can make your own version of advance that will never go past the end iterator. Adapted from https://en.cppreference.com/w/cpp/iterator/advance
// Assumes c++11 at least
template<class It, class Distance>
constexpr void clamped_advance(It& it, It limit, Distance n) {
using category = typename std::iterator_traits<It>::iterator_category;
static_assert(std::is_base_of<std::input_iterator_tag, category>::value, "Cannot advance non-input iterator");
auto dist = typename std::iterator_traits<It>::difference_type(n);
assert(("Can only clamped_advance forward", dist >= 0));
if (std::is_base_of<std::random_access_iterator_tag, category>::value)
std::advance(it, std::min(dist, std::distance(it, limit)));
else
while (dist-- > 0 && it != limit) ++it;
}
And you can use it like:
for (auto it = myVector.begin(); it != myVector.end(); ++it) {
// Do some stuff
// If this was going to advance past the end, `it` would equal `myVector.end()`
// and the loop will stop
clamped_advance(it, myVector.end(), variableAmount);
}
I would begin by getting the distance between the current iterator and the begin iterator.
If that distance plus the offset is larger than the vector size, you're going out of bounds.

Starting a map iterator not at city.begin()

In short, when trying to iterate through a map, is it possible to start the iterator at an index/key that isn't *.begin()?
I have a map of cities with a class "City". (City has coordinates for the city, and in the following code, calc_dist(c1, c2) will calculate the distance between the coordinates). What I am trying to do is create a "2D Map" (i.e. map<string, map<string, double>> dist) that can access the distance between the city by using dist[city1][city2].
to compute the distances I basically create a nested iterator over the cities, and it works, but it's slow when using many cities. since the distance between cities are symmetric, i can cut the loops in half by storing the distance in the reverse of the map.
what i was hoping to do was start the second iterator at the current city from the first iterator. http://www.cplusplus.com/reference/map/map/ tells me that the order is preserved so I feel like I should be able to do this.
Sample code:
// Function create_distance_chart(...)
map<string, map<string, double>> create_distance_chart(map<string, City> c){
map<string, map<string, double>> dist;
for (map<string, City>::iterator it = c.begin(); it != c.end(); ++it){
for (map<string, City>::iterator it2 = c.begin(); it2 != c.end(); ++it2) { // here i can make improvements, i hope
//calculate distance
dist[c[it->first]][c[it2->first]] = calc_dist(c[it->first],c[it2->first])// store in map
dist[c[it2->first]][c[it->first]] = calc_dist(c[it->first],c[it2->first])// store in map in the other direction.
}
}
}
in the line
for (map<string, City>::iterator it2 = c.begin(); it2 != c.end(); ++it2) {
i tried to change c.begin to c[it->first], c.at(it->first), just it->first, and a dummy variable that pulls the index for it->first.
the only other method i'm considering is doing a reverse iterator for the second iterator and having a termination condition that might cause the second loop to end before it2 != c.end() (i.e. at the first iterator's city), but i'm not making headway in that domain right now.
Thanks in advance!
First of all this statment:
c[it->first]
is a slow and convoluted way to simply say:
it->second
and as you use that 8 times in your loop there is no surprise it is slow indeed.
And for your loop, looks like you want to change second loop to:
for (map<string, City>::iterator it2 = std::next(it); it2 != c.end(); ++it2)
Note: if you do not have intention to change values in the map it is cleaner to use std::map::const_iterator instead.
Note2: I assumed that calculating distance btw a city and itself is meaningless. If it is not the case in your geometry then remove std::next() in above code and just assign it to it2 in the second loop initialization.

Iterate up to and including position

Probably very simple but can't get my head around it atm
I have this
// standard std::map and std::map::iterator
auto pos = map.find(val);
for(auto it = map.begin; it != pos; ++it)
I want to search for an element and then process all elements before (container is ordered, so just in iteration order) and including the find location, however this doesn't appear to examine the final element at position 'pos'. How can I achieve this?
Just move things around.
auto pos = map.find(val);
for(auto it = map.begin(); it != map.end(); ++it)
{
// Do something
if (it == pos)
break;
}
If it's possible that the value does not exist in the map, this will simply end up iterating over the entire map and that's why it != map.end(); is explicitly needed, to catch this particular runaway train...
Caution: if you have explicit continues inside the for loop, some additional TLC will be needed.
You need to differentiate between the cases where val is found in map and otherwise:
void f(/*map::[const_]iterator*/ it);
auto pos = map.find(val);
for (auto it = map.begin(); it != pos; ++it)
f(it);
if (pos != map.end())
f(pos);
You could use a do...while() loop but you would have to check for pos!=end() each time.

How to reach elements in a std::set two by two in C++

I have a list of integers.(Currently stored in a std::vector but to increase efficieny, I need to convert it to set. But in current version, I use it as following: (I'm using c++98 not c++11)
int res=0;
vector<vector<int> >costMatrix;
vector<int>partialSolution;
for(int i =0;i<partialSolution.size()-1;i++){
res+=costMatrix[partialSolution.get(i)][partialSolution.get(i+1)];
}
So, I need to do the same thing with the set data structure. But I dont know how to get two elements from the set at a time. I can get the partialSolution.get(i) with the code below but I could not get the partialSolution.get(i+1). Is there anyone to help me to modify the code below?
// this time set<int> partialSolution
int res=0;
std::set<int>::iterator it;
for (it = partialSolution.begin(); it != partialSolution.end(); ++it)
{
res+=costMatrix[*it][];
}
This could work (iterating from begin() to end()-1 and using std::next or ++ to get item next to current one).
In C++11:
for (it = partialSolution.begin(); it != std::prev(partialSolution.end()); ++it)
{
res+=costMatrix[*it][*(std::next(it))];
}
In C++98:
std::set<int>::iterator last = partialSolution.end();
--last;
for (it = partialSolution.begin(); it != last; ++it)
{
// not optimal but I'm trying to make it easy to understand...
std::set<int>::iterator next = it;
++next;
res+=costMatrix[*it][*next];
}

boost unordered_multimap loop over the unique keys

What is the easiest way to loop over the unique keys in a boost unordered_multimap.
For example I have this:
std::set<int> used;
for (auto p : valuesMap)
{
if (used.count(p.first))
continue;
used.insert(p.first);
auto range = valuesMap.equal_range(p.first);
if (p.first)
for (auto v = range.first; v != range.second; ++v)
//do something;
}
Is there better way to do that. It seems like the unique keys should be already known to the unordered map.
What you want to do is find a way to get the iterator following a certain key. In multimap I'd usually use upper_bound. But since unordered_multimap doesn't have that - I'll have to use equal_range.second:
for (auto iter=valueMap.begin();
iter!=valueMap.end();
iter=ValueMap.equal_range(iter->first)->second){
uniq_key=iter->first;
// Do whatever you want with uniq_key
}
But your example is weird to me - because you DO go over all the element . If I wanted to write your code, doing what you do, this is how I'd do it:
for (auto iter=valueMap.begin()
iter!=valueMap.end();
){ // Notice the lack of ++iter!!!
auto end=valueMap.equal_range(ier->first)->second;
for (;iter!=end;++iter)
// Do something
}