C++ understanding iter and erase - c++

I am just looking at a code, and I must say that I don't fully understand it yet.
vector<long>::iterator iter1;
vector<long>::iterator iter2;
while(m_vPitchMarks[0]<=vPitchPeriode[0])
{
iter1 = m_vPitchMarks.begin();
iter2 = vPitchPeriode.begin();
m_vPitchMarks.erase(iter1);
vPitchPeriode.erase(iter2);
if((m_vPitchMarks.size()==0)||(vPitchPeriode.size()==0))
break;
}
I am trying to break it down:
Here we will do a while-statement while the value of the first element of m_vPitchMarks is smaller than the value of the first element of vPitchPeriod.
while(m_vPitchMarks[0]<=vPitchPeriode[0])
{
}
Here we set something like a reference to the first element (element at index [0]) of both vectors.
iter1 = m_vPitchMarks.begin();
iter2 = vPitchPeriode.begin();
Now we erase all elements from m_vPitchMarks<> that have just this value.
For example, if iter1 had a value of 15, all elements in m_vPitchMarks<> that also have the value of 15 will be deleted, and the vector becomes shortened.
m_vPitchMarks.erase(iter1);
vPitchPeriode.erase(iter2);
Is that correct?
Thank you.

Almost as you say this:
while(m_vPitchMarks[0]<=vPitchPeriode[0])
will loop while the first value of m_vPitchMarks is less than or equal to the first value of vPitchPeriode.
Your explanation for this this code:
iter1 = m_vPitchMarks.begin();
iter2 = vPitchPeriode.begin();
m_vPitchMarks.erase(iter1);
vPitchPeriode.erase(iter2);
is not quite right. It simply removes the first value from both the vectors.
If you were using a std::deque or a std::list instead then you would just call pop_front to get the same result.
Then if we're out of values stop looping:
if((m_vPitchMarks.size()==0)||(vPitchPeriode.size()==0))
break;
Seems a particularly inefficient route given erasing an element from the front of a vector requires shifting every element that is left.

Related

Looping backwards starting with a found value

Suppose I have a map<int, set<int>> called my_map and I want to start at the element with key value 5 and loop backward until I get to the beginning, and check each set for a certain criterion. (I need to go backward, because I want to break if the criterion is met.)
std::find returns a forward iterator, so the way I thought of to do it is
for (auto rit = std::make_reverse_iterator(++my_map.find(A[i])); rit != my_map.rend(); ++rit) {
//check conditions on rit->second
}
Is that the best way?

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)

Find which element is not sorted in a list

I have a list filled with the numbers 3, 7, 10, 8, 12. I'd like to write a line that will tell me which element in the list is not sorted (in this case it is the 4th element). However, the code I have right now tells me the value of the 4th element (8). Is there a way I can rewrite this to tell me it's the 4th element rather than the number 8?
Here is the code I have now:
list<int>::iterator i;
if (!is_sorted(myList.begin(), myList.end())) {
i = is_sorted_until(myList.begin(), myList.end());
cout << *i << endl;
}
The first thing I should say, is that if you care about numerical position, you should be using a random access container, such as std::vector. Then your job would be simple:
// calling is_sorted is a waste if you're about to call is_sorted_until
auto i = is_sorted_until(my_vector.begin(), my_vector.end());
if (i != my_vector.end())
cout << (i - my_vector.begin());
If you must use a list, and you still need the position, then you should write your own algorithm which provides this information. It really shouldn't be that hard, it's just a for loop comparing each element to the one that precedes it. When you find one which compares less than the one which procedes it, you've found your element. Just keep an integer count alongside it, and you're good.
The obvious way would be to simply search for an element that's less than the element that preceded it.
int position = 1;
auto prev = myList.begin(), pos=std::next(prev, 1);
while (pos != myList.end() && *prev < *pos) {
++position;
++prev;
++pos;
}
You could use a standard algorithm instead, but they seem somewhat clumsy for this situation.
Does this help?
std::is_sorted_until()
From http://www.cplusplus.com/reference/algorithm/is_sorted_until/:
Find first unsorted element in range
Returns an iterator to the first element in the range [first,last) which does not follow an ascending order.
The range between first and the iterator returned is sorted.
If the entire range is sorted, the function returns last.
The elements are compared using operator< for the first version, and comp for the second.

traversing through a list

im trying to traverse through a list 2 values at a time, but for some reason, its getting stuck in an infinite loop
i have:
list<mystruct> a, b; // defined, each guaranteed to have at least 1 value
a.insert(a.end(), b.begin(), b.end());
a.sort(mysort);
list<mystruct>::iterator it1 = a.begin(), it2 = it1// or = a.begin(), it doesnt seem to make a difference
it2++;
while(it2 != a.end()){// im not sure if this condition is correct; probably the error
if (<condition>){
// stuff
a.erase(it2);
}
else{
it1++;
it2++;
}
}
say the combined list a is {1,2,3,3,4,5,6,6,6,7} and that i am trying to remove duplicates. i am trying to get *i = 1 and *j = 2 at first and then shift down so *i = 2 and *j = 3. what did i do wrong in this code??
im new to c++ lists, so sorry if this question sounds silly
You want to use it2 = a.erase(it2); otherwise it2 will be pointing to an element that you've erased from the list. a.erase returns the element following it2.
Since your list appears to be sorted and you want to remove duplicates, use unique:
a.unique();
Then you don't have to mess with iterators, erasing, etc.
See http://www.cplusplus.com/reference/stl/list/unique/

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