I have a vector that holds items that are either active or inactive. I want the size of this vector to stay small for performance issues, so I want items that have been marked inactive to be erased from the vector. I tried doing this while iterating but I am getting the error "vector iterators incompatible".
vector<Orb>::iterator i = orbsList.begin();
while(i != orbsList.end()) {
bool isActive = (*i).active;
if(!isActive) {
orbsList.erase(i++);
}
else {
// do something with *i
++i;
}
}
The most readable way I've done this in the past is to use std::vector::erase combined with std::remove_if. In the example below, I use this combination to remove any number less than 10 from a vector.
(For non-c++0x, you can just replace the lambda below with your own predicate:)
// a list of ints
int myInts[] = {1, 7, 8, 4, 5, 10, 15, 22, 50. 29};
std::vector v(myInts, myInts + sizeof(myInts) / sizeof(int));
// get rid of anything < 10
v.erase(std::remove_if(v.begin(), v.end(),
[](int i) { return i < 10; }), v.end());
I agree with wilx's answer. Here is an implementation:
// curFiles is: vector < string > curFiles;
vector< string >::iterator it = curFiles.begin();
while(it != curFiles.end()) {
if(aConditionIsMet) {
it = curFiles.erase(it);
}
else ++it;
}
You can do that but you will have to reshuffle your while() a bit, I think. The erase() function returns an iterator to the element next after the erased one: iterator erase(iterator position);. Quoting from the standard from 23.1.1/7:
The iterator returned from a.erase(q)
points to the element immediately
following q prior to the element being
erased. If no such element exists,
a.end() is returned.
Though maybe you should be using the Erase-remove idiom instead.
erase returns a pointer to the next iterator value (same as Vassilis):
vector <cMyClass>::iterator mit
for(mit = myVec.begin(); mit != myVec.end(); )
{ if(condition)
mit = myVec.erase(mit);
else
mit++;
}
If someone need working on indexes
vector<int> vector;
for(int i=0;i<10;++i)vector.push_back(i);
int size = vector.size();
for (int i = 0; i < size; ++i)
{
assert(i > -1 && i < (int)vector.size());
if(vector[i] % 3 == 0)
{
printf("Removing %d, %d\n",vector[i],i);
vector.erase(vector.begin() + i);
}
if (size != (int)vector.size())
{
--i;
size = vector.size();
printf("Go back %d\n",size);
}
}
As they said, vector's iterators get invalidated on vector::erase() no matter which form of iterator increment you use. Use an integer index instead.
You might want to consider using a std::list instead of a std::vector for your data structure. It is safer (less bug prone) to use when combining erasure with iteration.
Removing items from the middle of a vector will invalidate all iterators to that vector, so you cannot do this (update: without resorting to Wilx's suggestion).
Also, if you're worried about performance, erasing items from the middle of a vector is a bad idea anyway. Perhaps you want to use an std::list?
Related
In my code I used an array, I decided to try std :: set. I did this with an array:
for (int i=0; i<100; i++)
{
drawNMS(true, myMassive[myStr+i]);
}
myStr is added by buttons.
For std :: set, as I understand it, I need to use an iterator.
I try to do so:
std::set <int>::iterator iter;
for(iter=mySet.begin(); iter!=mySet.end();iter++)
{
drawNMS(true, myStr+*iter);
}
Where to insert for (int i = 0; i <100; i ++) correctly so that the end result is like when working with an array?
If you want to iterate over first 100 elements of a set, then do this:
std::set <int>::iterator iter;
int counter = 0;
for(iter=mySet.begin(); iter!=mySet.end() && counter < 100;++iter, ++counter)
{
drawNMS(true, myStr+*iter);
}
If you can use C++20, there is std::ranges which provides std::ranges::advance that works with a boundary, so your end-iterator can be found like this (not tested):
auto endSet100 = std::ranges::advance(mySet.begin(), 100, mySet.end());
This end-iterator can then be used like the original end-iterator. If you use std::advance, you have to do the boundary check yourself and the advance works on the iterator itself without returning a new iterator.
For simpler looping:
for (const auto& elem: std::ranges::subrange(mySet.begin(), std::ranges::advance(mySet.begin(), 100, mySet.end()))
{
drawNMS(true, myStr+elem);
}
It may be a bit longer, but it states: loop a subrange from the beginning to the 100th element or all if less elements are present.
I have a vector that holds items that are either active or inactive. I want the size of this vector to stay small for performance issues, so I want items that have been marked inactive to be erased from the vector. I tried doing this while iterating but I am getting the error "vector iterators incompatible".
vector<Orb>::iterator i = orbsList.begin();
while(i != orbsList.end()) {
bool isActive = (*i).active;
if(!isActive) {
orbsList.erase(i++);
}
else {
// do something with *i
++i;
}
}
The most readable way I've done this in the past is to use std::vector::erase combined with std::remove_if. In the example below, I use this combination to remove any number less than 10 from a vector.
(For non-c++0x, you can just replace the lambda below with your own predicate:)
// a list of ints
int myInts[] = {1, 7, 8, 4, 5, 10, 15, 22, 50. 29};
std::vector v(myInts, myInts + sizeof(myInts) / sizeof(int));
// get rid of anything < 10
v.erase(std::remove_if(v.begin(), v.end(),
[](int i) { return i < 10; }), v.end());
I agree with wilx's answer. Here is an implementation:
// curFiles is: vector < string > curFiles;
vector< string >::iterator it = curFiles.begin();
while(it != curFiles.end()) {
if(aConditionIsMet) {
it = curFiles.erase(it);
}
else ++it;
}
You can do that but you will have to reshuffle your while() a bit, I think. The erase() function returns an iterator to the element next after the erased one: iterator erase(iterator position);. Quoting from the standard from 23.1.1/7:
The iterator returned from a.erase(q)
points to the element immediately
following q prior to the element being
erased. If no such element exists,
a.end() is returned.
Though maybe you should be using the Erase-remove idiom instead.
erase returns a pointer to the next iterator value (same as Vassilis):
vector <cMyClass>::iterator mit
for(mit = myVec.begin(); mit != myVec.end(); )
{ if(condition)
mit = myVec.erase(mit);
else
mit++;
}
If someone need working on indexes
vector<int> vector;
for(int i=0;i<10;++i)vector.push_back(i);
int size = vector.size();
for (int i = 0; i < size; ++i)
{
assert(i > -1 && i < (int)vector.size());
if(vector[i] % 3 == 0)
{
printf("Removing %d, %d\n",vector[i],i);
vector.erase(vector.begin() + i);
}
if (size != (int)vector.size())
{
--i;
size = vector.size();
printf("Go back %d\n",size);
}
}
As they said, vector's iterators get invalidated on vector::erase() no matter which form of iterator increment you use. Use an integer index instead.
You might want to consider using a std::list instead of a std::vector for your data structure. It is safer (less bug prone) to use when combining erasure with iteration.
Removing items from the middle of a vector will invalidate all iterators to that vector, so you cannot do this (update: without resorting to Wilx's suggestion).
Also, if you're worried about performance, erasing items from the middle of a vector is a bad idea anyway. Perhaps you want to use an std::list?
Below I have a function which is supposed to extract from a vector into another vector the odd numbers and in the old vector i want to insert the even numbers at the beginning of the vector so that later to resize is. I know it is not really an effective way but I have a problem from a book to test the speed of the variant with resize instead of erasing, which I think would have the same speed anyway.. My problem is that in else when I want to insert the element which even, it does not work in this way.. What am I doing wrong in the insert function? Am I passing in a wrong way the iterator?
std::vector<int> extract(std::vector<int>& even)
{
std::vector<int> odd;
std::vector<int>::size_type size = even.size();
std::vector<int>::const_iterator a = even.begin();
std::vector<int>::const_iterator b = even.end();
while (a != b)
{
if ((*a) % 2 != 0)
{
odd.push_back((*a));
}
else
{
even.insert(even.begin(),(*a));
}
a++;
}
//even.resize(size);
return odd;
}
I was asked the following question in a 30-minute interview:
Given an array of integers, remove the duplicates without using any STL containers. For e.g.:
For the input array [1,2,3,4,5,3,3,5,4] the output should be:
[1,2,3,4,5];
Note that the first 3, 4 and 5 have been included, but the subsequent ones have been removed since we have already included them once in the output array. How do we do without using an extra STL container?
In the interview, I assumed that we only have positive integers and suggested using a bit array to mark off every element present in the input (assume every element in the input array as an index of the bit array and update it to 1). Finally, we could iterate over this bit vector, populating (or displaying) the unique elements. However, he was not satisfied with this approach. Any other methods that I could have used?
Thanks.
Just use std::sort() and std::unique():
int arr[] = { 1,2,3,4,5,3,3,5,4 };
std::sort( std::begin(arr), std::end(arr) );
auto end = std::unique( std::begin(arr), std::end(arr) );
Live example
We can first sort the array then check if the next element is equal to the previous one and finally give the answer with the help of another array of size 2 larger than the previous one like this.
Initialize the second array with a value that first array will not take (any number larger/smaller than the limit given) ,suppose 0 for simplicity then
int arr1[] = { 1,2,3,4,5,3,3,5,4 };
int arr2[] = { 0,0,0,0,0,0,0,0,0,0,0 };
std::sort( std::begin(arr1), std::end(arr1) );
int position=1;
arr2[0] = arr1[0];
for(int* i=begin(arr1)+1;i!=end(arr1);i++){
if((*i)!=(*(i-1))){
arr2[position] = (*i);
position++;
}
}
int size = 0;
for(int* i=begin(arr2);i!=end(arr2);i++){
if((*i)!=(*(i+1))){
size++;
}
else{
break;
}
}
int ans[size];
for(int i=0;i<size;i++){
ans[i]=arr2[i];
}
Easy algorithm in O(n^2):
void remove_duplicates(Vec& v) {
// range end
auto it_end = end(v);
for (auto it = begin(v); it != it_end; ++it) {
// remove elements matching *it
it_end = remove(it+1, it_end, *it);
}
// erase now-unused elements
v.erase(it_end, end(v));
}
See also erase-remove idiom
Edit: This is assuming you get a std::vector in, but it would work with C-style arrays too, you would just have to implement the erasure yourself.
I trying to brush up on my C++ since as it has been a while since I used it and I'm having a problem with storing pointers. At the end of the method below, the vector "graph" has all of the vertices I inserts, but the edges that were supposed to be added are corrupt (I can see the edges in the debugger, but their data is garbage). I was wondering if someone could help guide?
Thanks!
For reference, the add_edge function is declared as follows:
std::vector<vertice*> m_edges;
...
...
void add_edge(vertice* n);
{
m_edges.push_back(n);
}
The primary problem:
std::vector<vertice> graph;
std::string line;
//Iterate over line
int lineCount = 0;
while(getline(file, line))
{
auto vertices = parse_line(line);
for(size_t i = 0; i < vertices.size(); ++i)
{
auto result = std::find_if(
graph.begin(),
graph.end(),
[&] (vertice cVert) { return cVert.getName() == vertices.at(i); });
std::vector<vertice>::size_type currPos =
std::distance(graph.begin(), result);
if(result == graph.end())
{
graph.emplace_back(vertice{vertices.at(i)});
}
if(i == 0) { continue; }
graph.at(lineCount).add_edge(currPos == graph.size() ?
&graph.back() : &graph.at(currPos));
}
++lineCount;
}
//The vector graph has corrupt nodes here
Pointers to the content of a std::vector may be invalidated when you insert new elements, such as in std::vector::emplace_back:
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
Consider std::vector::reserve to reserve a capacity before needing to reallocate the elements or use different container, one which does not invalidate references on insert, perhaps std::list.
The problem lies here:
auto v = currPos == graph.size() ? &graph.back() : &graph.at(currPos);
You are taking the address of the graph elements, however if (when) your graph vector resizes during the emplace_back, you end up with dangling pointers. A solution is to fix the size of the vector and instead of push_back/emplace_back, to use directly std::vector::operator[], or to reserve enough memory via std::vector::reserve.
Probably unrelated:
graph.emplace_back(vertice{vertices.at(i)});
should just be
graph.emplace_back(vertices.at(i));
as otherwise you are invoking a copy constructor, see the definition of std::vector::emplace_back.