I have a stl set of integers and I would like to iterate through all unique pairs of integer values, where by uniqueness I consider val1,val2 and val2,val1 to be the same and I should only see that combination once.
I have written this in python where I use the index of a list (clusters):
for i in range(len(clusters) - 1):
for j in range(i+1,len(clusters)):
#Do something with clusters[i],clusters[j])
but without an index I am not sure how I can achieve the same thing with a stl set and iterators. I tried out:
for (set<int>::iterator itr = myset.begin(); itr != myset.end()-1; ++itr) {
cout << *itr;
}
but this fails as an iterator doesn't have a - operator.
How can I achieve this, or must I use a different container?
How about something along the following lines:
for(set<int>::const_iterator iter1 = myset.begin(); iter1 != myset.end(); ++iter1) {
for(set<int>::const_iterator iter2 = iter1; ++iter2 != myset.end();) {
{
std::cout << *iter1 << " " << *iter2 << "\n";
}
}
This yields all N*(N-1)/2 unique pairs, where N is the number of integers in your set.
As an aside: use a const_iterator whenever you iterate over a container without modifying anything, it's good style and might have better performance.
EDIT: Modified the code to reflect the suggestion made by Steve Jessop.
You don't need to do end() - 1 since end() is an iterator that points after the last element in the container.
The corrected code is:
for (set<int>::iterator itr = myset.begin(); itr != myset.end(); ++itr) {
for (set<int>::iterator itr2 = itr + 1; itr2 != myset.end(); ++itr2) {
// Do whatever you want with itr and itr2
}
}
Put your data in a boost::bimap, then iterate it both ways, copying the results into a standard STL map which will enforce uniqueness.
Related
This is the code I have. It is correct.
set<float> set1;
set1.insert(1);
set1.insert(2);
set1.insert(3);
set<float>::iterator it1;
for (it1 = set1.begin(); it1 != set1.end(); it1 ++)
{
cout << *it1 <<" ";
}
But why doesn't the following code work?
set<float> set1;
set1.insert(1);
set1.insert(2);
set1.insert(3);
set<float>::iterator it1;
for (it1 = set1.begin(); it1 != set1.end()-1; it1 ++)
{
cout << *it1 <<" ";
}
The iterators of an std::set are bidirectional. That means they support single steps in either direction (it++, it-- etc), but do not support increments of arbitrary length (it + N etc).
This is likely because it would be an inefficient O(N) operation, given that std::set is usually implemented as a self balancing binary search tree.
You can increment/decrement a bidirectional iterator by an arbitrary amount by using std::advance, std::next or std::prev:
std::advance(it, 42);
it = std::next(it, 42);
it = std::prev(it, 42);
operator- and operator+ is not supported for set iterators which are not of random access type, but are bidirectional type.
Ref : Here
The iterator type returned by set<>::begin() has no operator+(int) specified. However, in your example you can use the operator--() instead:
for (it1 = set1.begin(); it1 != --set1.end(); ++it1)
{
cout << *it1 << " ";
}
I ran into the following problem using std::multimap::equal_range() and insert().
According to both cplusplus.com and cppreference.com, std::multimap::insert does not invalidate any iterators, and yet the following code causes an infinite loop:
#include <iostream>
#include <map>
#include <string>
int main(int argc, char* argv[])
{
std::multimap<std::string,int> testMap;
testMap.insert(std::pair<std::string,int>("a", 1));
testMap.insert(std::pair<std::string,int>("a", 2));
testMap.insert(std::pair<std::string,int>("a", 3));
auto range = testMap.equal_range(std::string("a"));
for (auto it = range.first; it != range.second; ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
// this loop becomes infinite
}
// never gets here
for (auto it = testMap.begin(); it != testMap.end(); ++it)
{
std::cout << it->first << " - " << it->second << std::endl;
}
return 0;
}
The intent is to take all existing items in the multimap with a particular key ("a" in this case) and duplicate them under a second key ("b"). In practice, what happens is that the first loop never exits, because it never ends up matching range.second. After the third element in the map is processed, ++it leaves the iterator pointing at the first of the newly inserted items.
I've tried this with VS2012, Clang, and GCC and the same thing seems to happen in all compilers, so I assume it's "correct". Am I reading too much into the statement "No iterators or references are invalidated."? Does end() not count as an iterator in this case?
multimap::equal_range returns a pair whose second element in this case is an iterator to the past-the-end element ("which is the past-the-end value for the container" [container.requirements.general]/6).
I'll rewrite the code a bit to point something out:
auto iBeg = testMap.begin();
auto iEnd = testMap.end();
for(auto i = iBeg; i != iEnd; ++i)
{
testMap.insert( std::make_pair("b", i->second) );
}
Here, iEnd contains a past-the-end iterator. The call to multimap::insert doesn't invalidate this iterator; it stays a valid past-the-end iterator. Therefore the loop is equivalent to:
for(auto i = iBeg; i != testMap.end(); ++i)
Which is of course an infinite loop if you keep adding elements.
The end-iterator range.second is not invalidated.
The reason that the loop is infinite, is that each repetition of the loop body:
inserts a new element at the end of the map, thus increasing the distance between it and the end by one (so, after this insert, range no longer represents the equal_range for the key "a" because you have inserted a new key within the range it does represent, from the first "a" to the end of the container).
increments it, reducing the distance between it and the end by one.
Hence, it never reaches the end.
Here's how I might write the loop you want:
for (auto it = testMap.lower_bound("a"); it != testMap.end() && it->first == "a"; ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
}
A solution to make it work as expected (feel free to improve, it's a community wiki)
auto range = testMap.equal_range(std::string("a"));
if(range.first != range.second)
{
--range.second;
for (auto it = range.first; it != std::next(range.second); ++it)
{
testMap.insert(std::pair<std::string,int>("b", it->second));
}
}
This loop changes the iterators while running:
std::vector<int> c;
c.push_back(1);
c.push_back(2);
std::vector<int>::iterator iter = c.begin();
std::vector<int>::iterator endIter = c.end();
while( iter != endIter )
{
std::cout << (*iter) << std::endl;
iter = c.erase(iter);
}
It does not work because:
Iterators and references to the erased elements and to the elements between them and the end of the container are invalidated. Past-the-end iterator is also invalidated
How can I rewrite this (without using std::list, and using the while loop) ?
By the way, I know that auto has been implemented since C++11. Why would it be beneficial to use it ?
Simply do not cache the end iterator that will be invalidated:
while( iter != c.end() )
{
std::cout << (*iter) << std::endl;
iter = c.erase(iter);
}
or clear the vector after printing:
for(const auto& i : c) {
std::cout << i << std::endl;
}
c.clear();
Erasing an element changes end(). Change the loop:
while( iter != c.end())
Either
Rewrite it as
while( iter != c.end() )
{
std::cout << (*iter) << std::endl;
iter = c.erase(iter);
}
and the code will no longer rely on any potentially invalidated iterators,
or
"Refresh" any potentially invalidated iterators after each invalidating operation
while( iter != endIter )
{
std::cout << (*iter) << std::endl;
iter = c.erase(iter);
endIter = c.end();
}
These are the two generic approaches typically used in cases like that.
A more idiomatic way of doing this...
while(c.begin() != c.end()) c.erase(c.begin());
Though this is very slow, as a vectors underlying implementation uses a contiguous array(with extra space on the end). So repeatedly erasing the begin element is very ineficient, as every element ends up getting copied one space in the array earlier, n - index times! You can jurastically increase performance by doing this:
while(c.begin() != c.end()) c.pop_back();
My STL is a bit rusty, so forgive me for asking a possibly trivial question. Consider the following piece of code:
map<int,int> m;
...
for (auto itr = m.begin(); itr != m.end(); ++itr) {
if (itr->second == 0) {
m.erase(itr);
}
}
The question is: Is it safe to erase elements while looping over the map?
Yes, but not the way you do it. You're invalidating itr when you erase, then incrementing the invalid iterator.
auto itr = m.begin();
while (itr != m.end()) {
if (itr->first == 0) {
m.erase(itr++);
} else {
++itr;
}
}
I think that you shouldn't use removed iterator at all - in case of lists this causes serious problems, shouldn't be different for maps.
EDIT by Matthieu M: this code is well-formed in C++0x and allowed as an extension by MSVC.
map<int,int> m;
...
auto itr = m.begin();
while (itr != m.end())
{
if (itr->second == 0) {
itr = m.erase(itr);
}
else
{
itr++;
}
}
For the example given, It would actually be easier to use the erase overload that takes a key as an argument. This function erases all elements in the map with the given key (for a map, this is always either zero or one element)
map<int,int> m;
// ...
m.erase(0); // erase all elements with key equivalent to 0
I'm new to C++. I'd like to know how experienced coders do this.
what I have:
set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
s.insert(5);
for(set<int>::iterator itr = s.begin(); itr != s.end(); ++itr){
if (!(*itr % 2))
s.erase(itr);
}
and of course, it doesn't work. because itr is incremented after it is erased.
does it mean Itr has to point to the begin of the set everytime after i erase the element from the set?
for(set<int>::iterator itr = s.begin(); itr != s.end(); ){
if (!(*itr % 2))
s.erase(itr++);
else ++itr;
}
effective STL by Scott Myers
Erasing an element from std::set only invalidates iterators pointing to that element.
Get an iterator to the next element before erasing the target element.
You don't need to go back to the start. set::erase only invalidates iterators that refer to the item being erased, so you just need to copy the iterator and increment before erasing:
for(set<int>::iterator itr = s.begin(); itr != s.end();)
{
set<int>::iterator here = itr++;
if (!(*here % 2))
s.erase(here);
}
The best way is to use the combination of remove_if and erase
s.erase(remove_if(s.begin(), s.end(), evenOddFunctor), s.end())
This will be helpful
http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Erase-Remove
Also Refer to effective STL by scott meyers
Edit: Although my solution is wrong i am not deleting it. It might be a good learning for someone like me who does not about mutable/immutable iterators