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
Related
ALL,
std::map<int, std::string> addressee;
std::map<int, std::string>::iterator it1, it2;
for( it1 = addressee.begin(); it1 != addressee().end(); it1++ )
{
bool found = false;
for( it2 = it1 + 1; it2 != addressee.end() && !found; it2++ )
{
if( it1->second == it1->second )
{
printf( "Multiple occurences of addressees found" );
found = true;
}
}
}
gcc spits out an error: no match for operator+.
This code is a simplified version of what I'm trying to do right now. I guess I can use std::advance(), but it seems it just going to be a waste of the function call.
Is there a better fix for that?
std::map does not have random access iterators, only bidirectional iterators, so there's no + n operation. Instead, use std::next:
#include <iterator>
#include <map>
// ...
for (auto it1 = addressee.begin(), e = addressee.end(); it1 != e; ++it1)
{
for (auto it2 = std::next(it1); it2 != e; ++it2)
{
if (it1->second == it2->second)
{
// ...
break;
}
}
}
In fact, you should always use std::next, since it knows which iterator category its argument has and what the most efficient way to compute the next iterator is. That way, you don't have to care about the specific container you happen to be using.
#Kerrek has already pointed out how to handle the problem you're having at the syntactic level.
I'm going to consider the problem at a more algorithmic level--what you're really trying to accomplish overall, rather than just looking at how to repair that particular line of the code.
Unless the collection involved is dependably tiny so the efficiency of this operation doesn't matter at all, I'd make a copy of the mapped values from the collection, then use sort and unique on it to see if there are any duplicates:
std::vector<std::string> temp;
std::transform(addressee.begin(), addressee.end(),
std::back_inserter(temp),
[](std::pair<int, std::string> const &in) { return in.second; });
std::sort(temp.begin(), temp.end());
if (std::unique(temp.begin(), temp.end()) != temp.end()) {
std::cout << "Multiple occurrences of addressees found";
found = true;
}
This reduces the complexity from O(N2) to O(N log N), which will typically be quite substantial if the collection is large at all.
I want to know if the following check is necessary:
std::list<int> myList;
.....
for (std::list<int>::iterator itr = myList.begin(); itr != myList.end(); ++itr)
{
if (itr != NULL) // Is This Check Necessary?
{
// DO SOMTHING.
}
}
I have seen this check in some places and I'm wondering if that is a necessary check.
Maybe this check is necessary if the list is an input to a function?
Thanks,
Ofer.
No, it is an unnecessary check.
You might want to check *itr != nullptr, if the list held some kind of pointers.
No, it's not necessary. I am not sure it's even valid.
instead you can use new range based for loops (C++11), so you dont need to deal with iterator checking.
std::list<int> YourList;
for( auto z : YourList ) {
//
std::cout << z; // z is int
// amazing codes and fun :)
//
}
It's unnecessary to check.If the myList is empty,then myList.begin() equals to myList.end()
I think there's no way to test if an iterator is uninitialized,
But you can initialize iterator to the end() of the container as a default value.
std::list<int>::iterator itr = yourlist.end();
//
if (itr != yourlist.end())
{
//doSomething
}
I want to loop through a vector and erase certain elements that correspond to a certain criteria, for example:
vector<int> myvector;
vector<int>::iterator it;
myvector.push_back(1);
myvector.push_back(2);
myvector.push_back(3);
myvector.push_back(4);
for(it = myvector.begin(); it != myvector.end(); ++it){
if((*it) == 4){
it = myvector.erase(it);
}
}
Now this works fine unless the criterion erases the last item like in the code above. How do you avoid this behaviour ?
Thanks.
EDIT------------------------------------
Now the reason I was looping through it was that there are actually 4 vectors I need to delete the element from (but the criterion is only on one vector):
In this case, is this how to go ?
vector<int> myvector;
vector<int> myvector2;
vector<int> myvector3;
vector<int> myvector4;
vector<int>::iterator it;
vector<int>::iterator it2;
vector<int>::iterator it3;
vector<int>::iterator it4;
myvector.push_back(1);
myvector.push_back(2);
myvector.push_back(3);
myvector.push_back(4);
(assume myvector2/3/4 have values inside them)
it2 = myvector2.begin()
it3 = myvector3.begin()
it4 = myvector4.begin()
for(it = myvector.begin(); it != myvector.end();){
if((*it) == 4){
it = myvector.erase(it);
it2 = myvector2.erase(it2);
it3 = myvector3.erase(it3);
it4 = myvector4.erase(it4);
}
else{
++it;
++it2;
++it3;
++it4;
}
}
Is there a modification to the erase/remove idiom valid in this case ?
The usual is the remove/erase idiom, which would look something like this:
myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());
Edit: Rereading your question, you mention "certain criteria". If the criteria aren't necessarily just removing a single value, you can use std::remove_if instead of std::remove, and specify your criteria in a functor.
Edit2: for the version dealing with four vectors, the usual method is to create a struct holding the four related values, and delete entire structs:
struct x4 {
int a, b, c, d;
// define equality based on the key field:
bool operator==(x4 const &other) { return a == other.a; }
x4(int a_, int b_=0, int c_=0, ind d_=0) : a(a_), b(b_), c(c_), d(d_) {}
};
std::vector<x4> myvector;
myvector.erase(std::remove(myvector.begin(), myvector.end(), x4(4));
Again, if your criteria are more complex than you can easily express in a comparison operator, you can use std::remove_if instead of std::remove. This is also useful if/when you might need to apply different criteria at different times.
If you really need to keep your data in parallel vectors (e.g., you're feeding the data to something external that requires separate, contiguous arrays), then using a loop is probably as good as the alternatives.
Don't do this with a for loop, there's already a well-debugged algorithm for you.
myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());
I think you should write the loop as :
for(it = myvector.begin(); it != myvector.end(); )
{
if((*it) == 4)
it = myvector.erase(it);
else
++it; //increment here!
}
Because in your code, if you find 4, you update it in the if block itself, but after that you again increment/update it in the for also which is wrong. That is why I moved it to else block that ensures that it gets incremented if you don't find 4 (or whatever value you're searching).
Also remember that erase returns iterator pointing to the new location of the element that followed the last element erased by the function call.
erase is generally used with remove (Also have a look at erase-remove idiom) as shown below
myvector.erase(std::remove(myvector.begin(), myvector.end(), 4), myvector.end());
for(it = myvector.begin(); it < myvector.end(); ++it){
if((*it) == 4){
it = myvector.erase(it);
}
}
This will make sure your loop will break if the it >= myvector.end().
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 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.