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.
Related
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.
How can we iterating over the map as if it was a linear data structure and also tell me how we simply put in the key to get the value associated.?
You should use iterators to iterate over the map:
std::map<int, int> myMap;
for (auto iter = myMap.begin(); iter != myMap.end(); ++iter)
// do something with iter
or you can use the loop syntax:
for (auto& myMapPair : myMap)
// do something with the pair
Note that for the first version you can access the key with iter->firstand the value with iter->secondas iter is an iterator to a pair. For the second version you directly have a reference to a pair so you should use iter.first and iter.second.
As for finding the value from a key you should do:
auto iter = myMap.find(50);
if (iter == myMap.end()) // didn't find anything
// do something
else // you found something
// do something
I have a map<int, string>. The keys refer to client nodes.
I need to traverse the map, and compare each key to every other key held within a map against a boolean function (which checks if the nodes are connected).
I.e. what is the best way to do something like
map<int, string> test_map;
map<int, string>::iterator iter;
for (iter = test_map.begin(); iter!=test_map.end(); iter++)
{
int curr_node = iter->first;
/* psuedo-code:
1. iterate through other keys
2. check against boolean e.g. bool fn1(curr_node, test_node) returns true if nodes are connected
3. perform fn2 if true */
}
I'm not sure how to do the iteration part with the other keys in the nodes - much thanks in advance.
The completely naive solution is this:
map<int, string>::iterator iter, iter2;
for ( iter = test_map.begin(); iter != test_map.end(); iter++)
{
int curr_node = iter->first;
for ( iter2 = test_map.begin(); iter2 != test_map.end(); iter2++)
{
if( iter == iter2 ) continue;
int test_node = iter2->first;
if( fn1(curr_node, test_node) ) fn2();
}
}
Taking a step back, perhaps you'd be better served by a slightly different data structure here?
An adjacency list or matrix might work better, at least for this task you're asking about.
The gist is that you'd have an edge-centric, not a node-centric, data structure. That would make your stated task of calling fn2 on every pair of connected nodes very easy.
Let me know if this approach makes sense given your requirements and I'll be happy to include more details or references.
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.
Let's say I have a sequential container, and a range (pair of iterators) within that container of elements that are currently 'active'. At some point, I calculate a new range of elements that should be active, which may overlap the previous range. I want to then iterate over the elements that were in the old active range but that are not in the new active range to 'deactivate' them (and similarly iterate over the elements that are in the new range but not the old range to 'activate' them).
Is this possible?
Does it become easier if I know that the start of the new active range will always be later in the container than the start of the old active range?
For the purposes of the question, assume the container is a vector.
You can use two sets for the last active range and another for the current active range. Use the set_difference algorithm to get the objects to be activated/deactivated.
What you need is a range_difference function. I though Boost.Range would provide something like this, but I didn't find anything in their doc (well, I didn't search very thoroughly...), so I rolled up my own.
The following function returns a pair of range, containing the result of the difference between the range denoted by (first1,last1) and the one denoted by (first2,last2). A pre-condition is that first1 must be positioned before or at the same position as first2.
template <typename InputIterator>
std::pair<
std::pair<InputIterator, InputIterator>,
std::pair<InputIterator, InputIterator> >
range_difference(InputIterator first1, InputIterator last1,
InputIterator first2, InputIterator last2)
{
typedef std::pair<InputIterator, InputIterator> Range;
InputIterator it;
// first1 must be <= first2
for (it = first1 ; it != last1 && it != first2 ; ++it);
Range left_range = std::make_pair(first1, it); // Left range
if (it == last1)
return std::make_pair(left_range, std::make_pair(first2, first2));
// it == first2
while (it != last1 && it != last2) ++it;
return std::make_pair(left_range, std::make_pair(it, last1)); // Right range
}
The result of the difference can be composed of two parts, if range2 is completely included into range1. You end up with a left range and a right range:
|_____________________|__________________|________________________|
first1 first2 last2 last1
In this case, the function returns (first1, first2),(last2, last1).
In this other configuration,
|_____________________| |________________________|
first1 last1 first2 last2
the function returns (first1, last1),(first2, first2). There are many other possible configurations. However, one important thing to know is that in the case where the right range is empty, it will be positioned at max(first2, last1). You'll see how this is necessary in the example.
Finally, if first1 and first2 are at the same position, the returned left range will be empty, i.d. (first1,first1).
Now, how can we use this function to solve your problem? Well that's rather easy for the "deactivate" range, but a little trickier for the "activate" one:
typedef std::vector<Activable>::iterator Iterator;
Iterator old_beg, old_end, new_beg, new_end; // Old and new ranges
typedef std::pair<Iterator, Iterator> Range;
typedef std::pair<Range, Range> SplitRange;
SplitRange deactivate = range_difference(old_beg, old_end, new_beg, new_end);
// Left range
for (Iterator it = deactivate.first.first;
it != deactivate.first.second;
++it)
it->deactivate();
// Right range
for (Iterator it = deactivate.second.first;
it != deactivate.second.second;
++it)
it->deactivate();
SplitRange activate =
range_difference(new_beg, new_end, new_beg, deactivate.second.first);
// Note the use of the previously returned right range -------^
for (Iterator it = activate.second.first;
it != activate.second.second;
++it)
it->activate();
And there you go. Maybe this solution is a little overkill to your problem, but I think the range_difference function could be useful in many place.
Here's a simple solution:
typedef std::pair<std::vector<T>::iterator, std::vector<T>::iterator> Range;
void Update(std::vector<T>& v, Range oldActive, Range newActive)
{
int op = 0;
for (std::vector<T>::iterator i = v.begin(), end = v.end(); i != end; ++i)
{
if (i == oldActive.first) op += 1;
if (i == oldActive.second) op -= 1;
if (i == newActive.first) op += 2;
if (i == newActive.second) op -= 2;
if (op == 1) i->Deactivate();
if (op == 2) i->Activate();
}
}
This deliberately puts simplicity before efficiency as a starting point, since it scans the entire vector; on the other hand it's single pass and does no copying.
I think I'll keep it simple:
// Iterators denoting the old and new ranges (might be cleaner to use some kind
// of typedef like James Hopkin's did, but that's not the most important)
std::vector<Activable>::iterator old_beg,
old_end,
new_beg,
new_end;
std::vector<Activable>::iterator it;
// Deactivate
for (it = old_beg ; // go from the beginning of the old range
it != old_end && it != new_beg ; // to either the end of the old one or the
++it) // beginning of the new one
it->deactivate();
// "Jump" to the correct position
if (it == old_end) it = new_beg; // no overlap
else it = old_end; // overlap
// Activate
for (; it != new_end ; ++it)
it->activate();
You'll note that I assumed that the new range couldn't be totally contained into the old one (e.g. you can't have an old range going from index 4 to 10, and a new one going from 5 to 7). If this is a case, you'll need to change a little the algorithm.