Looping back near the beggining using std::vector iterators - c++

I have an iterator that needs to loop near the beginning of the vector whenever it reaches its end, for the amount it went over the end, like so:
std::vector<int> vec = {...}, vec1;
std::vector<int>::iterator it = vec.begin();
for(;vec.size() != 0;){
it += k; //k is a const integer
if(it >= vec.end()){
it -= items.end(); // something like that, but this syntax is obviously invalid
}
vec1.push_back(*it);
it = vec.erase(it);
}
So perhaps I increment vec{1,2,3,4,5} by 3, it should first remove 3, then 1 and put them in vec1, in a sense it loops around by however was left when it reached the end. I tried a bunch of different syntaxes, but there's always a type mismatch error. Is there an elegant way of doing this?

XY-solution: I recommend keeping an index instead of an iterator and use the remainder operator.
for(std::size_t i = 0; vec.size() != 0;){
i = (i + k) % vec.size();
vec1.push_back(vec[i]);
vec.erase(vec.begin() + i);
}
So perhaps I increment vec{1,2,3,4,5} by 3, it should first remove 3
This doesn't match your attempted code. If you increment iterator to first element by 3 and erase it, then you'd be erasing the element with value 4.

Related

C++ - unordered_map operator [], unexpected behavior

This is a simple script I was working on, but I failed to understand why it was behaving unexpectedly.
Basically, I had an array of integers with duplicates, and I wanted to store the number of times an element occurred in the array along with the element's value in an unordered_map,
Then, for each entry in the map {k, v}, I needed to determine if k + 1 existed in the array, and if so, do something with it.
Below you can see the code.
vector<int> A = {1, 1, 3, 2, 5, 3};
for (int i = 0; i < A.size(); ++i) m[A[i]]++;
int ans = 0;
for (const auto& e: m) {
if (m[e.first + 1] > 0) ans = max(ans, e.second + m[e.first + 1]);
}
Everything seemed to work. However, when k + 1 did not exist in the unordered_map, the loop would just terminate, and I do not understand why.
According to c++ documentation, the operator [] inserts a new element if it does not exist . But that does not tell me anything about the loop just not working.
I suspect it has something to do with the fact that I am modifying the unordered_map inside the loop. If this is the case, could you guys elaborate more on that?
I really appreciate your help.
Using m[e.first + 1] inside your loop will insert a new element into m if it does not exist, which will cause problems for the loop itself because a range-based for loop uses iterators internally, and altering a std::unordered_map while you are iterating through it with iterators is undefined behavior, as an insertion may invalidate the iterators:
If an insertion occurs and results in a rehashing of the container, all iterators are invalidated. Otherwise iterators are not affected. References are not invalidated. Rehashing occurs only if the new number of elements is greater than max_load_factor()*bucket_count().
To avoid that, use the map's find() method instead to check for a key's existence without inserting it:
for (const auto& e: m) {
auto iter = m.find(e.first + 1);
if (iter != m.end()) ans = max(ans, e.second + iter->second);
}

How do i use the for loop for a specified range while using maps in c++

I want to use the for loops with iterators while using maps and want to run it for a specified range not from begin() end end(). I would want to use it for a range like from 3rd element to 5th element
I would want to use it for a range like from 3rd element to 5th
element
Since std::map's iterator is not a RandomAccessIterator, but only a BidirectionalIterator (you cannot write .begin() + 3), you can use std::next for this purpose:
for (auto it = std::next(m.begin(), 2); it != std::next(m.begin(), 5); ++it)
{
// ...
}
And remember - check your ranges to ensure, that you iterate over a valid scope.
This code should be pretty optimal and safe for corner cases:
int count = 0;
for( auto it = m.begin(); it != m.end(); ++it ) {
if( ++count <= 3 ) continue;
if( count > 5 ) break;
// use iterator
}
but the fact you are iterating an std::map this way shows most probably you are using a wrong container (or your logic for 3rd to 5th element is wrong)

Going through and inserting data to std vectors

I never had this kind of problems when using QVector, but unfortunately I cannot use qt in this one. What I am trying to do is to go through 2 vectors which contains points X and Y as doubles.
I am trying to insert some x and y points in between the points that are predefined. The problem is that after second loop everything goes bad, iterators start to get weird values and the points are inserted in the 0 index all the time. This iterator thing is really confusing, I can't understand why I can't insert to my vectors simply using intiger indexes. I tried the C++11 way as well, but didn't give me anything.
The m_x vector represents time in seconds and m_y vector represents temperature. m_x is double, but it can only take integer positive values.
int CProcessData::calculateMidPoints()
{
if((0 == m_x.size()) || (0 == m_y.size()))
return 1;
std::vector<double>::iterator itX = m_x.begin();
std::vector<double>::iterator itY = m_y.begin();
double midPointY = 0;
for (uint32_t i = 0; i < (m_x.size() - 1); i++)
{
if (i != m_x[i])
{
m_x.insert(itX, (double)i);
midPointY = (m_y[i - 1] * 1 + m_y[i] * (1 / (m_x[i + 1] - m_x[i]))) /
(1 + (1 / (m_x[i + 1] - m_x[i])));
m_y.insert(itY, midPointY);
}
else
{
itX++;
itY++;
}
}
return 0;
}
calling insert() is going to invalidate your iterators.
From http://en.cppreference.com/w/cpp/container/vector/insert:
Causes reallocation if the new size() is greater than the old capacity(). If the new size() is greater than capacity(), all iterators and references are invalidated. Otherwise, only the iterators and references before the insertion point remain valid. The past-the-end iterator is also invalidated.
The insert() function returns a iterator to the inserted element. Since this is a valid iterator you can capture it and use it for the next iteration. Here is a little example of how that works:
std::vector<int> data = { 1, 2, 3, 4, 5 };
auto it = data.begin();
std::advance(it, 3);
for (int i = 0; i < 3; i++)
{
it = data.insert(it, 9);
++it;
}
data will contain { 1, 2, 3, 9, 9, 9, 4, 5 } at the end of the for loop.

Why can't I delete last element of vector

I have stl vector consisting of several elements. I need to iterate through this vector and delete elements which meets some criteria. So I wrote this code
for (int j = imageDataVector.size()-1; j >= 0; j--) {
if(imageDataVector[i] < threshold)
imageDataVector.erase(imageDataVector.end() - j);
}
This code works fine for almost all cases, however if all elements of vector meets the criteria I get an error:
vector erase iterator outside the range
This error occurs if I have only one element left in the vector. What do I do wrong ?
if(imageDataVector[i] < threshold)
imageDataVector.erase(imageDataVector.end()-j);
Should likely be:
if(imageDataVector[j] < threshold)
imageDataVector.erase(imageDataVector.begin()+j);
EDIT: for completeness, the erase-remove way and the iterator way:
imageDataVector.erase(std::remove_if(imageDataVector.begin(), imageDataVector.end(), std::bind2nd(std::less<vector_data_type>(), threshold)), imageDataVector.end());
vector<type>::iterator it = imageDataVector.begin();
while (it != imageDataVector.end()) {
if (*it < threshold)
it = imageDataVector.erase(it);
else
++it;
}
You're mixing forward and backward indexing.
I'd consider using std::remove_if instead. That way if you're removing multiple elements you don't shift the entire vector forwards on each erase.
It would look something like this:
imageDataVector.erase(std::remove_if(imageDataVector.begin(), imageDataVector.end(), std::bind2nd(std::less<data_type>(), threshold)), imageDataVector.end());
Alternately try the following, noting that it will result in a lot of movement if you remove multiple items from the vector.
for (int j=imageDataVector.size()-1 ;j>=0;j--)
{
if(imageDataVector[i] < threshold)
imageDataVector.erase(imageDataVector.begin()+j);
}
You're trying to count down j to zero, and imageDataVector.end() - 0 is not a valid iterator. In the standard C++ library containers, the end iterator points one past the last element, not at the last element.

std::remove not working correctly, still has extra elements

std::vector<int> v = {1,2,3,4,5};
auto i = std::remove(v.begin(),v.end(),3);
for(auto j = v.begin(); j!= v.end();++j)
std::cout << *j;
Actual output : 12455
Where does extra 5 come from?
Desired output : 1245
How to achieve this?
remove doesnt actually remove the elements
Remove removes from the range [first, last) all elements that are equal to value. That is, remove returns an iterator new_last such that the range [first, new_last) contains no elements equal to value. 1 The iterators in the range [new_last, last) are all still dereferenceable, but the elements that they point to are unspecified. Remove is stable, meaning that the relative order of elements that are not equal to value is unchanged.`
std::remove algorithm works only using a pair of forward iterators and in general knows nothing about the underlying container.
You need to use the erase-remove idiom to actually remove the element i.e combine erase with remove
auto i = std::remove(v.begin(),v.end(),3);
v.erase(i,v.end());
for(auto j = v.begin(); j!= v.end();++j)
std::cout << *j;
Read the documentation for std::remove again.
The function does not remove elements from a container (in fact, it doesn't even know that a container is involved, as it only sees iterators), it merely moves values in a sequence and return a new iterator i such that all the interval [ begin .. i [ contains all non-removed elements in the original order. Elements left over in [ i .. end [ are unspecified, and it is your responsibility to eliminate that interval from a container (if you need it):
auto i = std::remove(...);
v.erase(i,v.end());
The reason why you have an additional 5 is that the typical removal algorithm copies values into holes left by removed values, and since values past the i iterator are never overwritten, they remain the same as in the original sequence. This behavior, however, is not reliable - just eliminate the values past i without reading them.
remove returns the new end. So the fix of your code is this:
std::vector<int> v = {1,2,3,4,5};
auto newEnd = std::remove(v.begin(),v.end(),3);//return value stored in newEnd
for(auto j = v.begin(); j!= newEnd ;++j) //note j!=newEnd
std::cout << *j;
Output:
1245
Check it out yourself : http://www.ideone.com/3AMD9
Actually, std::remove moves at beginning of the range all elements which don't satisfy predicate, AND returns iterator of the new end.
So usage is erase-remove idiom:
v.erase(std::remove(v.begin(), v.end(), 3), v.end());
In c++20, it can be simplified thanks to std::erase:
std::erase(v, 3);
It's seams that you are printing the n+1 position of the vector in the for() statement.
It should be:
for(auto j = v.begin(); j!= v.end();j++)
std::cout << *j;
j++ no ++j