Best way to further split some elements in vector - c++

Here is a sample vector
vector<string> v;
v.push_back("one");
v.push_back("two");
v.push_back("three four");
v.push_back("five");
// print
one
two
three four
five
i need to split the element number 3 so now the vector look (without creating another vector)
// print
one
two
three
four
five
Should i use iterator or simple loop? it should have good performance too.

As has been pointed out in the comments, if you're inserting
elements into the vector (which you will be), you have to avoid
using an invalidated iterator. Still, something along the lines
of the following should work:
std::vector<std::string> iter = v.begin();
while ( iter != v.end() ) {
std::string::iterator breakpoint = std::find( iter->begin(), iter->end(), ' ' );
if ( breakpoint == iter->end() ) {
++ iter;
} else {
std::string next( breakpoint + 1, iter->end() );
iter->erase( breakpoint, iter->end() );
iter = v.insert( iter + 1, next );
}
}
In such cases, I generally prefer generating into a copy,
however.

Related

Looping through std::vector and deleting elements results in out of range exception [duplicate]

I have a vector of IInventory*, and I am looping through the list using C++11 range for, to do stuff with each one.
After doing some stuff with one, I may want to remove it from the list and delete the object. I know I can call delete on the pointer any time to clean it up, but what is the proper way to remove it from the vector, while in the range for loop? And if I remove it from the list will my loop be invalidated?
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
for (IInventory* index : inv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
}
No, you can't. Range-based for is for when you need to access each element of a container once.
You should use the normal for loop or one of its cousins if you need to modify the container as you go along, access an element more than once, or otherwise iterate in a non-linear fashion through the container.
For example:
auto i = std::begin(inv);
while (i != std::end(inv)) {
// Do some stuff
if (blah)
i = inv.erase(i);
else
++i;
}
Every time an element is removed from the vector, you must assume the iterators at or after the erased element are no longer valid, because each of the elements succeeding the erased element are moved.
A range-based for-loop is just syntactic sugar for "normal" loop using iterators, so the above applies.
That being said, you could simply:
inv.erase(
std::remove_if(
inv.begin(),
inv.end(),
[](IInventory* element) -> bool {
// Do "some stuff", then return true if element should be removed.
return true;
}
),
inv.end()
);
You ideally shouldn't modify the vector while iterating over it. Use the erase-remove idiom. If you do, you're likely to encounter a few issues. Since in a vector an erase invalidates all iterators beginning with the element being erased upto the end() you will need to make sure that your iterators remain valid by using:
for (MyVector::iterator b = v.begin(); b != v.end();) {
if (foo) {
b = v.erase( b ); // reseat iterator to a valid value post-erase
else {
++b;
}
}
Note, that you need the b != v.end() test as-is. If you try to optimize it as follows:
for (MyVector::iterator b = v.begin(), e = v.end(); b != e;)
you will run into UB since your e is invalidated after the first erase call.
Is it a strict requirement to remove elements while in that loop? Otherwise you could set the pointers you want to delete to NULL and make another pass over the vector to remove all NULL pointers.
std::vector<IInventory*> inv;
inv.push_back( new Foo() );
inv.push_back( new Bar() );
for ( IInventory* &index : inv )
{
// do some stuff
// ok I decided I need to remove this object from inv...?
if (do_delete_index)
{
delete index;
index = NULL;
}
}
std::remove(inv.begin(), inv.end(), NULL);
sorry for necroposting and also sorry if my c++ expertise gets in the way of my answer, but if you trying to iterate through each item and make possible changes (like erasing an index), try using a backwords for loop.
for(int x=vector.getsize(); x>0; x--){
//do stuff
//erase index x
}
when erasing index x, the next loop will be for the item "in front of" the last iteration. i really hope this helped someone
OK, I'm late, but anyway: Sorry, not correct what I read so far - it is possible, you just need two iterators:
std::vector<IInventory*>::iterator current = inv.begin();
for (IInventory* index : inv)
{
if(/* ... */)
{
delete index;
}
else
{
*current++ = index;
}
}
inv.erase(current, inv.end());
Just modifying the value an iterator points to does not invalidate any other iterator, so we can do this without having to worry. Actually, std::remove_if (gcc implementation at least) does something very similar (using a classic loop...), just does not delete anything and does not erase.
Be aware, however, that this is not thread safe(!) - however, this applies, too, for some of the other solutions above...
I will show with example, the below example remove odd elements from vector:
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 1
for(auto it = vecInt.begin();it != vecInt.end();){
if(*it % 2){// remove all the odds
it = vecInt.erase(it);
} else{
++it;
}
}
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
// recreate vecInt, and use method 2
vecInt = {0, 1, 2, 3, 4, 5};
//method 2
for(auto it=std::begin(vecInt);it!=std::end(vecInt);){
if (*it % 2){
it = vecInt.erase(it);
}else{
++it;
}
}
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
// recreate vecInt, and use method 3
vecInt = {0, 1, 2, 3, 4, 5};
//method 3
vecInt.erase(std::remove_if(vecInt.begin(), vecInt.end(),
[](const int a){return a % 2;}),
vecInt.end());
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
output aw below:
024
024
024
Keep in mind, the method erase will return the next iterator of the passed iterator.
From here , we can use a more generate method:
template<class Container, class F>
void erase_where(Container& c, F&& f)
{
c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
c.end());
}
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 4
auto is_odd = [](int x){return x % 2;};
erase_where(vecInt, is_odd);
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
See here to see how to use std::remove_if.
https://en.cppreference.com/w/cpp/algorithm/remove
In opposition to this threads title, I'd use two passes:
#include <algorithm>
#include <vector>
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
std::vector<IInventory*> toDelete;
for (IInventory* index : inv)
{
// Do some stuff
if (deleteConditionTrue)
{
toDelete.push_back(index);
}
}
for (IInventory* index : toDelete)
{
inv.erase(std::remove(inv.begin(), inv.end(), index), inv.end());
}
A much more elegant solution would be to switch to std::list (assuming you don't need fast random access).
list<Widget*> widgets ; // create and use this..
You can then delete with .remove_if and a C++ functor in one line:
widgets.remove_if( []( Widget*w ){ return w->isExpired() ; } ) ;
So here I'm just writing a functor that accepts one argument (the Widget*). The return value is the condition on which to remove a Widget* from the list.
I find this syntax palatable. I don't think I would ever use remove_if for std::vectors -- there is so much inv.begin() and inv.end() noise there you're probably better off using an integer-index-based delete or just a plain old regular iterator-based delete (as shown below). But you should not really be removing from the middle of a std::vector very much anyway, so switching to a list for this case of frequent middle of list deletion is advised.
Note however I did not get a chance to call delete on the Widget*'s that were removed. To do that, it would look like this:
widgets.remove_if( []( Widget*w ){
bool exp = w->isExpired() ;
if( exp ) delete w ; // delete the widget if it was expired
return exp ; // remove from widgets list if it was expired
} ) ;
You could also use a regular iterator-based loop like so:
// NO INCREMENT v
for( list<Widget*>::iterator iter = widgets.begin() ; iter != widgets.end() ; )
{
if( (*iter)->isExpired() )
{
delete( *iter ) ;
iter = widgets.erase( iter ) ; // _advances_ iter, so this loop is not infinite
}
else
++iter ;
}
If you don't like the length of for( list<Widget*>::iterator iter = widgets.begin() ; ..., you can use
for( auto iter = widgets.begin() ; ...
I think I would do the following...
for (auto itr = inv.begin(); itr != inv.end();)
{
// Do some stuff
if (OK, I decided I need to remove this object from 'inv')
itr = inv.erase(itr);
else
++itr;
}
you can't delete the iterator during the loop iteration because iterator count get mismatch and after some iteration you would have invalid iterator.
Solution:
1) take the copy of original vector
2) iterate the iterator using this copy
2) do some stuff and delete it from original vector.
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
std::vector<IInventory*> copyinv = inv;
iteratorCout = 0;
for (IInventory* index : copyinv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
inv.erase(inv.begin() + iteratorCout);
iteratorCout++;
}
Erasing element one-by-one easily leads to N^2 performance.
Better to mark elements that should be erased and erase them at once after the loop.
If I may presume nullptr in not valid element in your vector, then
std::vector<IInventory*> inv;
// ... push some elements to inv
for (IInventory*& index : inv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
{
delete index;
index =nullptr;
}
}
inv.erase( std::remove( begin( inv ), end( inv ), nullptr ), end( inv ) );
should work.
In case your "Do some stuff" is not changing elements of the vector and only used to make decision to remove or keep the element, you can convert it to lambda (as was suggested in somebody's earlier post) and use
inv.erase( std::remove_if( begin( inv ), end( inv ), []( Inventory* i )
{
// DO some stuff
return OK, I decided I need to remove this object from 'inv'...
} ), end( inv ) );

How to handle checked iterators?

for debug mode in VS2013 I receive an out of range exception if I add a constant value to an iterator and this iterator will go out of range afterwards.
For example:
#include <cstdlib>
#include <vector>
int main(void) {
std::vector<unsigned char> data(10, 0);
auto it = data.begin();
while (it != data.end()) {
if ((it + 3) <= data.end()) {
it += 3;
}
else {
it = data.end();
}
}
return EXIT_SUCCESS;
}
In the fourth run of the while-loop the check it + 3 <= data.end() fails and the exception is thrown.
A short workaround for the VS compiler would be to insert #define _ITERATOR_DEBUG_LEVEL 0 that disables the option for checked iterators in debug mode.
But I would appreciate a better, common solution that will work for both, VS and GCC compiler!
I'm sure there already exists a way by the STL to handle iterator distances and checks like this :) but I don't know it yet...
The better common solution is the following
while ( std::distance( it, data.end() ) >= 3 )
{
// some code
std::advance( it, 3 );
// some code
}
Or
while ( it != data.end() )
{
// some code
std::advance( it, std::min<int>( 3, std::distance( it, data.end() ) ) );
// some code
}
The choice between these two variants depends on whether you want to process the iterator at least one time even if data.end() - it is less than 3.
If a non-random access iterator is used then the loop can look as
while ( it != data.end() )
{
// some code
( ++it != data.end() ) && ( ++it != data.end() ) && ( ++it != data.end() );
// some code
}
That's because end() points to the element beyond the last actual element of the vector. So your condition if ((it + 3) <= data.end()) will eventually evaluate to end() + 1 (when it already equals end()), which is invalid. To fix it, change it to if ((it + 3) < data.end()).
To reiterate: the last available element of a vector, or any other STL container, is located at end() - 1. If the container is empty, begin() is the same as end(). It is always an error to dereference an iterator pointing to end().

How to iterate throgh a vector within vector?

I have a vector within a vector defined as:
std::vector<std::vector<myObj>> some_list(list_size);
some of the entries in some_list have elements and some don't those that have 3 elements have size 3 those with 4 have size 4 etc. for example:
some_list[0] {size=3}
some_list[1] {size=4}
but at some point there is an empty entry with 0 elements such as:
some_list[14] {size=0}
after which point every entry that follows has size 0 as well.
I am trying to resize my vector so it excludes such entries and is left with only the entries that have elements. But for some reason I can't make it work. Here's what I have:
int snum = 0;
for (std::vector<std::vector<myObj>>::iterator a_it = a_list.begin();
a_it != a_list.end(); a_it++) {
while (a_it->size() != 0) {
snum++;
}
}
a_list.resize(snum);
This line:
while (a_it->size() != 0) {
should probably read:
if (a_it->size() != 0) {
If you are sure all the entries after the first empty one will also be empty, you can use std::find_if to get an iterator to the first empty element, then use std::vector::erase:
#include <algorithm>
bool is_empty(const std::vector<myObj>& v) { return v.empty(); }
v.erase(std::find_if(v.begin(), v.end(), is_empty), v.end() );
or, if you have C++11 support, you can use a lambda:
v.erase(std::find_if(v.begin(),
v.end(),
[](const std::vector<myObj>& v){ return v.empty(); }),
v.end() );
where I used v is some_list.
Oherwise, if empty entries are not all at the end, you can remove the entries with zero elements using the erase remove idiom with a suitable function.
v.erase(std::remove_if(v.begin(), v.end(), is_empty), v.end() );
You should first remove the empty elements. You can do with with a ready-made algorithm:
#include <algorithm>
#include <functional>
some_list.erase(std::remove_if(some_list.begin(), some_list.end(),
std::mem_fn(&std::vector<myObj>::empty),
some_list.end());
Then you can resize: some_list.resize(N). In fact, if you want the new elements to be empty anyway, you can skip the erase step and only do the remove_if part.

Increment an iterator c++

My problem is as follows: I use an iterator, and I want to compare each element to the next element. Prototype looks like below, how can I increase the iterator to be able to compare?
Also, how can I set a proper condition for this to happen? I mean how to point on the last element, not on the next after the last like with end() function:
std::vector<T>::const_iterator it;
std::vector<T>::const_iterator it2;
for (it = set.begin(), it != set.end(); it++)
{
// some things happen
if ( final == it )
{
if ( it != set.end()-1 ) // how to write properly condition?
{
it2 = it + 1; //how to assign the next here?
if (...)//some condition
{
if ( it->func1() - it2->func1()) < 20 ) //actual comparison of two consecutive element values
// do something
}
}
}
}
In C++11 use the functions std::next() and std::prev().
Your code could become:
// before
it != std::set.end()-1
// after
it != std::prev(set.end())
and
// before
it2 = it + 1;
// after
it2 = std::next(it);
That is true also for non-vector containers, such as map,set or others.
NOTE: after std::next(it), "it" iterator remains unmodified!
NOTE 2: Use it2 = std::next(it,n); to increment as much as you need.
You can use adjacent_find to solve that. You should use the second form of that function (with predicate) and pass to the predicate your some things happen and some condition in c-tor
auto found = std::adjacent_find( set.begin(), set.end(),
[some_comdition]( const T & left, const T & right ) {
if ( some_comdition ) {
if ( left.func1() - right.func1() < 20 ) {
do_smth();
// return true; if there's no need to continue
}
}
return false;
}
);
Based on the fact that it++ is acceptable, we should define a new iterator called itplusone, which is initialized as itplusone = ++it. In this way, you can safely use the meaning of an iterator pointing to the next item of it. Also clearly, the range of iterator of itplusone bounded by terms itplusone != set.end(). I use this method to compute the total weight of a path, which is defined as a list object.
In the for loop, you use it++ which means it = it + 1, which is perfectly ok. So this one will be fine also it2 = it + 1. it2 will be pointing to the next value.
In the for loop again, you use it != set.end(), which is again perfectly ok. So you can also it + 1 < set.end(), just like you did in your code.
I don't see anything wrong in your code, just wanted to explain.
somewhat late, just discovered it, but like mentioned above, ++ iterator works fine.
vector<string> P
auto itA = begin(P);
while(itA != end(P))
{
if(itA != end(P))
{
++itA; //
}
}

Removing item from vector, while in C++11 range 'for' loop?

I have a vector of IInventory*, and I am looping through the list using C++11 range for, to do stuff with each one.
After doing some stuff with one, I may want to remove it from the list and delete the object. I know I can call delete on the pointer any time to clean it up, but what is the proper way to remove it from the vector, while in the range for loop? And if I remove it from the list will my loop be invalidated?
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
for (IInventory* index : inv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
}
No, you can't. Range-based for is for when you need to access each element of a container once.
You should use the normal for loop or one of its cousins if you need to modify the container as you go along, access an element more than once, or otherwise iterate in a non-linear fashion through the container.
For example:
auto i = std::begin(inv);
while (i != std::end(inv)) {
// Do some stuff
if (blah)
i = inv.erase(i);
else
++i;
}
Every time an element is removed from the vector, you must assume the iterators at or after the erased element are no longer valid, because each of the elements succeeding the erased element are moved.
A range-based for-loop is just syntactic sugar for "normal" loop using iterators, so the above applies.
That being said, you could simply:
inv.erase(
std::remove_if(
inv.begin(),
inv.end(),
[](IInventory* element) -> bool {
// Do "some stuff", then return true if element should be removed.
return true;
}
),
inv.end()
);
You ideally shouldn't modify the vector while iterating over it. Use the erase-remove idiom. If you do, you're likely to encounter a few issues. Since in a vector an erase invalidates all iterators beginning with the element being erased upto the end() you will need to make sure that your iterators remain valid by using:
for (MyVector::iterator b = v.begin(); b != v.end();) {
if (foo) {
b = v.erase( b ); // reseat iterator to a valid value post-erase
else {
++b;
}
}
Note, that you need the b != v.end() test as-is. If you try to optimize it as follows:
for (MyVector::iterator b = v.begin(), e = v.end(); b != e;)
you will run into UB since your e is invalidated after the first erase call.
Is it a strict requirement to remove elements while in that loop? Otherwise you could set the pointers you want to delete to NULL and make another pass over the vector to remove all NULL pointers.
std::vector<IInventory*> inv;
inv.push_back( new Foo() );
inv.push_back( new Bar() );
for ( IInventory* &index : inv )
{
// do some stuff
// ok I decided I need to remove this object from inv...?
if (do_delete_index)
{
delete index;
index = NULL;
}
}
std::remove(inv.begin(), inv.end(), NULL);
sorry for necroposting and also sorry if my c++ expertise gets in the way of my answer, but if you trying to iterate through each item and make possible changes (like erasing an index), try using a backwords for loop.
for(int x=vector.getsize(); x>0; x--){
//do stuff
//erase index x
}
when erasing index x, the next loop will be for the item "in front of" the last iteration. i really hope this helped someone
OK, I'm late, but anyway: Sorry, not correct what I read so far - it is possible, you just need two iterators:
std::vector<IInventory*>::iterator current = inv.begin();
for (IInventory* index : inv)
{
if(/* ... */)
{
delete index;
}
else
{
*current++ = index;
}
}
inv.erase(current, inv.end());
Just modifying the value an iterator points to does not invalidate any other iterator, so we can do this without having to worry. Actually, std::remove_if (gcc implementation at least) does something very similar (using a classic loop...), just does not delete anything and does not erase.
Be aware, however, that this is not thread safe(!) - however, this applies, too, for some of the other solutions above...
I will show with example, the below example remove odd elements from vector:
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 1
for(auto it = vecInt.begin();it != vecInt.end();){
if(*it % 2){// remove all the odds
it = vecInt.erase(it);
} else{
++it;
}
}
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
// recreate vecInt, and use method 2
vecInt = {0, 1, 2, 3, 4, 5};
//method 2
for(auto it=std::begin(vecInt);it!=std::end(vecInt);){
if (*it % 2){
it = vecInt.erase(it);
}else{
++it;
}
}
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
// recreate vecInt, and use method 3
vecInt = {0, 1, 2, 3, 4, 5};
//method 3
vecInt.erase(std::remove_if(vecInt.begin(), vecInt.end(),
[](const int a){return a % 2;}),
vecInt.end());
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
output aw below:
024
024
024
Keep in mind, the method erase will return the next iterator of the passed iterator.
From here , we can use a more generate method:
template<class Container, class F>
void erase_where(Container& c, F&& f)
{
c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
c.end());
}
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 4
auto is_odd = [](int x){return x % 2;};
erase_where(vecInt, is_odd);
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
See here to see how to use std::remove_if.
https://en.cppreference.com/w/cpp/algorithm/remove
In opposition to this threads title, I'd use two passes:
#include <algorithm>
#include <vector>
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
std::vector<IInventory*> toDelete;
for (IInventory* index : inv)
{
// Do some stuff
if (deleteConditionTrue)
{
toDelete.push_back(index);
}
}
for (IInventory* index : toDelete)
{
inv.erase(std::remove(inv.begin(), inv.end(), index), inv.end());
}
A much more elegant solution would be to switch to std::list (assuming you don't need fast random access).
list<Widget*> widgets ; // create and use this..
You can then delete with .remove_if and a C++ functor in one line:
widgets.remove_if( []( Widget*w ){ return w->isExpired() ; } ) ;
So here I'm just writing a functor that accepts one argument (the Widget*). The return value is the condition on which to remove a Widget* from the list.
I find this syntax palatable. I don't think I would ever use remove_if for std::vectors -- there is so much inv.begin() and inv.end() noise there you're probably better off using an integer-index-based delete or just a plain old regular iterator-based delete (as shown below). But you should not really be removing from the middle of a std::vector very much anyway, so switching to a list for this case of frequent middle of list deletion is advised.
Note however I did not get a chance to call delete on the Widget*'s that were removed. To do that, it would look like this:
widgets.remove_if( []( Widget*w ){
bool exp = w->isExpired() ;
if( exp ) delete w ; // delete the widget if it was expired
return exp ; // remove from widgets list if it was expired
} ) ;
You could also use a regular iterator-based loop like so:
// NO INCREMENT v
for( list<Widget*>::iterator iter = widgets.begin() ; iter != widgets.end() ; )
{
if( (*iter)->isExpired() )
{
delete( *iter ) ;
iter = widgets.erase( iter ) ; // _advances_ iter, so this loop is not infinite
}
else
++iter ;
}
If you don't like the length of for( list<Widget*>::iterator iter = widgets.begin() ; ..., you can use
for( auto iter = widgets.begin() ; ...
I think I would do the following...
for (auto itr = inv.begin(); itr != inv.end();)
{
// Do some stuff
if (OK, I decided I need to remove this object from 'inv')
itr = inv.erase(itr);
else
++itr;
}
you can't delete the iterator during the loop iteration because iterator count get mismatch and after some iteration you would have invalid iterator.
Solution:
1) take the copy of original vector
2) iterate the iterator using this copy
2) do some stuff and delete it from original vector.
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
std::vector<IInventory*> copyinv = inv;
iteratorCout = 0;
for (IInventory* index : copyinv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
inv.erase(inv.begin() + iteratorCout);
iteratorCout++;
}
Erasing element one-by-one easily leads to N^2 performance.
Better to mark elements that should be erased and erase them at once after the loop.
If I may presume nullptr in not valid element in your vector, then
std::vector<IInventory*> inv;
// ... push some elements to inv
for (IInventory*& index : inv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
{
delete index;
index =nullptr;
}
}
inv.erase( std::remove( begin( inv ), end( inv ), nullptr ), end( inv ) );
should work.
In case your "Do some stuff" is not changing elements of the vector and only used to make decision to remove or keep the element, you can convert it to lambda (as was suggested in somebody's earlier post) and use
inv.erase( std::remove_if( begin( inv ), end( inv ), []( Inventory* i )
{
// DO some stuff
return OK, I decided I need to remove this object from 'inv'...
} ), end( inv ) );