Let's say I have std::vector<std::pair<int,Direction>>.
I am trying to use erase-remove_if idiom to remove pairs from the vector.
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool { return stopPoint.first == 4; }));
I want to delete all pairs that have .first value set to 4.
In my example I have pairs:
- 4, Up
- 4, Down
- 2, Up
- 6, Up
However, after I execute erase-remove_if, I am left with:
- 2, Up
- 6, Up
- 6, Up
What am I doing wrong here?
The correct code is:
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool
{ return stopPoint.first == 4; }),
stopPoints.end());
You need to remove the range starting from the iterator returned from std::remove_if to the end of the vector, not only a single element.
"Why?"
std::remove_if swaps elements inside the vector in order to put all elements that do not match the predicate towards the beginning of the container. This means that if the predicate (body of the lambda function) returns true, then that element will be placed at the end of the vector.
remove_if then **returns an iterator which points to the first element which matches the predicate **. In other words, an iterator to the first element to be removed.
std::vector::erase erases the range starting from the returned iterator to the end of the vector, such that all elements that match the predicate are removed.
More information: Erase-remove idiom (Wikipedia).
The method std::vector::erase has two overloads:
iterator erase( const_iterator pos );
iterator erase( const_iterator first, const_iterator last );
The first one only remove the element at pos while the second one remove the range [first, last).
Since you forget the last iterator in your call, the first version is chosen by overload resolution, and you only remove the first pair shifted to the end by std::remove_if. You need to do this:
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool {
return stopPoint.first == 4;
}),
stopPoints.end());
The erase-remove idiom works as follow. Let say you have a vector {2, 4, 3, 6, 4} and you want to remove the 4:
std::vector<int> vec{2, 4, 3, 6, 4};
auto it = std::remove(vec.begin(), vec.end(), 4);
Will transform the vector into {2, 3, 6, A, B} by putting the "removed" values at the end (the values A and B at the end are unspecified (as if the value were moved), which is why you got 6 in your example) and return an iterator to A (the first of the "removed" value).
If you do:
vec.erase(it)
...the first overload of std::vector::erase is chosen and you only remove the value at it, which is the A and get {2, 3, 6, B}.
By adding the second argument:
vec.erase(it, vec.end())
...the second overload is chosen, and you erase value between it and vec.end(), so both A and B are erased.
I know that at the time this question was asked there was no C++20, so just adding answer for completeness & up-to-date answer for this question,
C++20 has now much cleaner & less verbose pattern, using std::erase_if.
See generic code example:
#include <vector>
int main()
{
std::vector<char> cnt(10);
std::iota(cnt.begin(), cnt.end(), '0');
auto erased = std::erase_if(cnt, [](char x) { return (x - '0') % 2 == 0; });
std::cout << erased << " even numbers were erased.\n";
}
Specific question code snippet:
std::erase_if(stopPoints, [&](const stopPointPair stopPoint)-> bool { return stopPoint.first == 4; });
see complete details here:
https://en.cppreference.com/w/cpp/container/vector/erase2
Related
Given two vectors of copyable elements and a predicate over those items, what is an efficient and idiomatic method for:
Removing matching items from the first vector
Appending matching items to the second vector
The following snippet reflects my current thinking, but it does require two passes over the source vector.
vector<int> source{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
vector<int> target;
auto predicate = [](int n) { return n % 2 == 0; };
std::for_each(source.begin(), source.end(), [&predicate, &target](int n) {
if (predicate(n)) {
target.push_back(n);
}
});
auto it = std::remove_if(source.begin(), source.end(), predicate);
source.erase(it, source.end());
I'd use std::partition to partition sourceinto two parts.
auto itr = std::partition(source.begin(), source.end(), predicate);
target.insert(target.end(), itr, source.end());
source.resize(itr - source.begin());
If the ordering needs to remain the same then use stable_partition.
I agree with #acraig5075, that stable_partition, followed by copy and erase will do what you want.
However, if you don't want to shuffle elements around in the source container (probably a futile wish, IMHO), you can do it in a loop: (untested code)
auto itr = source.begin();
while (itr != source.end()) {
if (predicate(*itr)) {
target.push_back(*itr);
itr = source.erase(itr);
} else
++itr;
}
Note that this will "slide down" the elements of elements one at a time as they are moved into target, which is why I opined that avoiding this was probably foolish. (Note: If you're working with list, then it is less foolish)
I'm trying to get a single element from a vector and push it to the back of the vector then remove it so I won't have an empty section in memory. The erase-remove idiom can do this but it removes all instances of a particular value. I just want the first one removed.
I'm not too experienced with standard library algorithms and I can't find the appropriate methods (if any) to do this. Here is an example:
int main() {
std::vector<int> v{1, 2, 3, 3, 4};
remove_first(v, 3);
std::cout << v; // 1, 2, 3, 4
}
So how would I go about removing the first occurance of a 3 from this vector?
Find it first, then erase it:
auto it = std::find(v.begin(),v.end(),3);
// check that there actually is a 3 in our vector
if (it != v.end()) {
v.erase(it);
}
If you don't care about maintaining the ordering of the elements in the vector, you can avoid the copy of the "tail" of remaining elements on erase:
auto it = std::find(v.begin(), v.end(), 3);
if (it != v.end()) {
std::iter_swap(it, v.end() - 1);
v.erase(v.end() - 1);
}
I wrote a code where I need to find lower_bound from square number sequence. But lower bound giving me result for upper_bound.
Here is my code & compiler link: http://cpp.sh/3cppb
// Example program
#include <iostream>
#include <string>
#include <algorithm>
int main()
{
std::vector<int> v{ 1, 4, 9, 16, 25 }; // all the square numbers
int x = std::lower_bound(v.begin(), v.end(), 5) - v.begin() ;
std:: cout<<"Postion "<<x<< " value "<<v[x] <<std::endl; //getting output for upperbound
}
Output:
Postion 2 value 9
Expected Output
Postion 1 value 4
std::lower_bound returns the iterator to the first element which is greater or equal to the target value:
Returns an iterator pointing to the first element in the range [first,
last) that is not less than (i.e. greater or equal to) value, or last
if no such element is found.
As 9 is the first value which is greater or equal to 5 (it is greater, of course), the result is totally correct.
If you tried to find an element which is already in v, like 9, then you would get different results for std::lower_bound and std::upper_bound:
std::distance(begin(v), std::lower_bound(begin(v), end(v), 9)); // 2
std::distance(begin(v), std::upper_bound(begin(v), end(v), 9)); // 3
std::lower_bound is working correctly. The function returns the first element that is not less than the value provided. Since 9 is the first value that is not less than 5 you get that element.
std::upper_bound in this case will return the same element as it returns the first element greater than the specified value. Where you will see a difference is cases like
std::vector data = {4,4,4};
auto low = std::lower_bound(data.begin(), data.end(), 4);
auto high = std::upper_bound(data.begin(), data.end(), 4);
In this case low will be begin() as 4 is not less than 4 while high will be end() as there is no element greater than 4 in the vector.
The quotation from the Standard, [lower.bound]:
template<class ForwardIterator, class T>
ForwardIterator lower_bound(ForwardIterator first, ForwardIterator last, const T& value);
Returns: The furthermost iterator i in the range [first,last] such that for every iterator j in the range [first,i) the following corresponding conditions hold: *j < value.
Let's say I have std::vector<std::pair<int,Direction>>.
I am trying to use erase-remove_if idiom to remove pairs from the vector.
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool { return stopPoint.first == 4; }));
I want to delete all pairs that have .first value set to 4.
In my example I have pairs:
- 4, Up
- 4, Down
- 2, Up
- 6, Up
However, after I execute erase-remove_if, I am left with:
- 2, Up
- 6, Up
- 6, Up
What am I doing wrong here?
The correct code is:
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool
{ return stopPoint.first == 4; }),
stopPoints.end());
You need to remove the range starting from the iterator returned from std::remove_if to the end of the vector, not only a single element.
"Why?"
std::remove_if swaps elements inside the vector in order to put all elements that do not match the predicate towards the beginning of the container. This means that if the predicate (body of the lambda function) returns true, then that element will be placed at the end of the vector.
remove_if then **returns an iterator which points to the first element which matches the predicate **. In other words, an iterator to the first element to be removed.
std::vector::erase erases the range starting from the returned iterator to the end of the vector, such that all elements that match the predicate are removed.
More information: Erase-remove idiom (Wikipedia).
The method std::vector::erase has two overloads:
iterator erase( const_iterator pos );
iterator erase( const_iterator first, const_iterator last );
The first one only remove the element at pos while the second one remove the range [first, last).
Since you forget the last iterator in your call, the first version is chosen by overload resolution, and you only remove the first pair shifted to the end by std::remove_if. You need to do this:
stopPoints.erase(std::remove_if(stopPoints.begin(),
stopPoints.end(),
[&](const stopPointPair stopPoint)-> bool {
return stopPoint.first == 4;
}),
stopPoints.end());
The erase-remove idiom works as follow. Let say you have a vector {2, 4, 3, 6, 4} and you want to remove the 4:
std::vector<int> vec{2, 4, 3, 6, 4};
auto it = std::remove(vec.begin(), vec.end(), 4);
Will transform the vector into {2, 3, 6, A, B} by putting the "removed" values at the end (the values A and B at the end are unspecified (as if the value were moved), which is why you got 6 in your example) and return an iterator to A (the first of the "removed" value).
If you do:
vec.erase(it)
...the first overload of std::vector::erase is chosen and you only remove the value at it, which is the A and get {2, 3, 6, B}.
By adding the second argument:
vec.erase(it, vec.end())
...the second overload is chosen, and you erase value between it and vec.end(), so both A and B are erased.
I know that at the time this question was asked there was no C++20, so just adding answer for completeness & up-to-date answer for this question,
C++20 has now much cleaner & less verbose pattern, using std::erase_if.
See generic code example:
#include <vector>
int main()
{
std::vector<char> cnt(10);
std::iota(cnt.begin(), cnt.end(), '0');
auto erased = std::erase_if(cnt, [](char x) { return (x - '0') % 2 == 0; });
std::cout << erased << " even numbers were erased.\n";
}
Specific question code snippet:
std::erase_if(stopPoints, [&](const stopPointPair stopPoint)-> bool { return stopPoint.first == 4; });
see complete details here:
https://en.cppreference.com/w/cpp/container/vector/erase2
I'm trying to get a single element from a vector and push it to the back of the vector then remove it so I won't have an empty section in memory. The erase-remove idiom can do this but it removes all instances of a particular value. I just want the first one removed.
I'm not too experienced with standard library algorithms and I can't find the appropriate methods (if any) to do this. Here is an example:
int main() {
std::vector<int> v{1, 2, 3, 3, 4};
remove_first(v, 3);
std::cout << v; // 1, 2, 3, 4
}
So how would I go about removing the first occurance of a 3 from this vector?
Find it first, then erase it:
auto it = std::find(v.begin(),v.end(),3);
// check that there actually is a 3 in our vector
if (it != v.end()) {
v.erase(it);
}
If you don't care about maintaining the ordering of the elements in the vector, you can avoid the copy of the "tail" of remaining elements on erase:
auto it = std::find(v.begin(), v.end(), 3);
if (it != v.end()) {
std::iter_swap(it, v.end() - 1);
v.erase(v.end() - 1);
}