This question already has an answer here:
Erasing from a std::vector while doing a for each?
(1 answer)
Closed 8 years ago.
Hello suppose that there is vector with 5 elements
vector<int> V;
for (int i=0;i<5;i++){
v.push_back(i);
}
Now how can I use find if and erase to erase values bigger than 2 from the vector ?
Would you please suggest a sample code I prepared sth but I am not sure about that.
THANKS
You can use the erase-remove idiom using std::remove_if and a suitable functor. For example
V.erase(std::remove_if(std::begin(V), std::end(V),
[](int i){ return i > 2; } ),
std::end(V) );
If you're stuck with a pre-C++11 compiler, then
bool func(int i) { return i > 2; }
V.erase(std::remove_if(V.begin(), V.end(), func), V.end());
You can remove from vector based on predicate (condition):
std::vector<int>::iterator it = std::remove_if( // it shifts, doesn't
v.begin(), v.end(), // reduces the physical size
[](int i){ return i > 2; } ));
and erase unneeded values:
v.erase( it, v.end()); // this is needed as remove_if doesn't erase
// elements that don't meet condition (are not to be
// removed), it simply moves those that don't meet
// condition to the begin.
// now we reduce the physical size
Why remove_if is followed by erase
Removing is done by shifting (by means of move assignment) the
elements in the range in such a way that the elements that are not to
be removed appear in the beginning of the range. Relative order of the
elements that remain is preserved and the physical size of the
container is unchanged. Iterators pointing to an element between the
new logical end and the physical end of the range are still
dereferenceable, but the elements themselves have unspecified values
(as per MoveAssignable post-condition). A call to remove is typically
followed by a call to a container's erase method, which erases the
unspecified values and reduces the physical size of the container to
match its new logical size.
http://en.cppreference.com/w/cpp/algorithm/remove
Example
// Let's imagine your vector is
v = { 1, 2, 3, 4, 5};
^ ^
begin end
// copy( v.begin(), v.end(), ostream_iterator<int>(cout));
will print: 12345
after remove_if vector becomes:
v = { 1, 2, 3, 4, 5};
^ ^ ^
begin it end
// copy( v.begin(), v.end(), ostream_iterator<int>(cout));
// will still print a vector of size 5: 12345
// but
// copy( v.begin(), it, ostream_iterator<int>(cout));
will print: 12
after v.erase( it, v.end()); vector becomes:
v = { 1, 2};
^ ^
begin end
// copy( v.begin(), v.end(), ostream_iterator<int>(cout));
will print: 12
compiled example
C++03:
bool f(int i) { return i > 2; }
std::vector<int>::iterator it = std::remove_if( v.begin(), v.end(), f); // shifts
v.erase( it, v.end()); // reduces the physical size
This is an alternate solution without using std::remove_if just using the erase method and iterators.
typedef std::vector<int> IntVector;
IntVector v;
// fill
for (IntVector::iterator it = v.begin(); it != v.end();) {
if (*it > 2) {
it = v.erase(it);
} else {
++it;
}
}
Related
This question already has answers here:
Repeat contents of a std::vector
(3 answers)
Closed 9 months ago.
I have a vector
vector<int>v = {1,2,3,4,5};
I'd like to repeat the elements in the vector for, say, 3 times, such that the vector becoms
v = {1,2,3,4,5, 1,2,3,4,5, 1,2,3,4,5};
EDIT: In fact, if I need to repeat the elements for many times, say 1000, obviously I have to come with something quick and light?
How do I do it?
This can be tricky. If you want to avoid creating a temporary working object you have to be careful to avoid invalidating iterators as you go. This should do it:
std::vector<int> v = {1, 2, 3, 4, 5};
// to avoid invalidating iterators, preallocate the memory
v.reserve(v.size() * 3);
// remember the end of the range to be duplicated
// (this is the iterator we don't want to invalidate)
auto end = std::end(v);
// insert two duplicates
v.insert(std::end(v), std::begin(v), end);
v.insert(std::end(v), std::begin(v), end);
for(auto i: v)
std::cout << i << '\n';
More generally you could modify this to add multiple duplicates like this:
std::vector<int> v = {1, 2, 3, 4, 5};
std::size_t const no_of_duplicates = 1000;
// to avoid invalidating iterators, preallocate the memory
v.reserve(v.size() * no_of_duplicates);
// remember the end of the range to be duplicated
// (this is the iterator we don't want to invalidate)
auto end = std::end(v);
// insert duplicates (start from one because already have the first)
for(std::size_t i = 1; i < no_of_duplicates; ++i)
v.insert(std::end(v), std::begin(v), end);
Use the insert method of vector class
v.insert(v.end(), v.begin(), v.end());
Use std::copy
std::vector<int> v = { 1 , 2, 3, 4, 5};
std::vector<int> r;
for (auto i = 0; i < 3; ++i) {
std::copy(v.begin(), v.end(), std::back_inserter(r));
}
v.swap(r);
I've written a simple function that removes elements from a vector (V2), based upon the values of another vector (V1):
std::vector<int> V1={6,2,3,4};
std::vector<int> V2={9,4,8,6,7};
for(int i=0; i<V1.size(); i++)
{
if(!V2.empty())
{
V2.erase(std::remove(V2.begin(),V2.end(),V1[i]),V2.end());
}
}
My challenge is that the above needs to be O(n) complexity. Currently this is O(n*m), n being V1, m being V2.
N.B. Arrays are not and cannot be sorted as the elements original index values are required.
Questions:
Am I right in saying 'V2.erase' is stopping this function from being O(n)? (Because its a nested iteration within the for loop).
Is there a way around this, by performing the erase operation outside the loop?
Why not use std::set_difference:
std::vector<int> test(
std::vector<int> v1,
std::vector<int>& v2)
{
// The algorithm we use requires the ranges to be sorted:
std::sort (v1.begin(), v1.end());
std::sort (v2.begin(), v2.end());
// our output vector: reserve space to avoid copying:
std::vector<int> v3;
v3.reserve (v2.size());
// Use std::set_difference to copy the elements from
// v2 that are not in v1 into v3:
std::set_difference (
v2.begin(), v2.end(),
v1.begin(), v1.end(),
std::back_inserter(v3));
return v3;
}
If v1.size() == n and v2.size() == m the runtime of this works out to roughly:
OK, so then how about this:
void test2(
std::vector<int> v1,
std::vector<int> v2)
{
// We can still sort this without affecting the indices
// in v2:
std::sort (v1.begin(), v1.end());
// Replace all the elements in v1 which appear in v2
// with -1:
std::replace_if (v2.begin(), v2.end(),
[&v1] (int v)
{
return std::binary_search(v1.begin(), v1.end(), v);
}, -1);
}
Not linear; estimating the complexity is left as an exercise for the OP.
A third alternative is this:
void test3(
std::vector<int> v1,
std::vector<int>& v2)
{
// We can still sort this without affecting the indices
// in v2:
std::sort (v1.begin(), v1.end());
auto ret = std::stable_partition (
v2.begin(), v2.end(),
[&v1] (int v)
{
return !std::binary_search(v1.begin(), v1.end(), v);
});
v2.erase (ret, v2.end());
}
Again, not linear, but options...
std::vector<int> V1={6,2,3,4};
std::vector<int> V2={9,4,8,6,7};
// must be a truly pathological case to have lookups of O(N)
std::unordered_set v1_hashed(v1.begin(), v1.end());
for(
size_t v2ix=v2.size()-1;
v2ix<v2.size(); // otherwise underflow past zero
v2ix--
) {
// generally, an O(1) is assumed here
if(v1_hashed.find(v2[v2ix]) != v1_hashed.end()) {
// removal of element will cost quite significant due to
// moving elements down. This is why doing the cycle in reverse
// (less elements to move down).
v2.erase(v2ix);
}
}
// O(N) searches + O(M) hashing of v1
I need to remove element from vector a and b, if these elements are equal. As I understand, when I remove an element, other elements move. So, a[1] become a[0], a[2] -- a[1] and so on. But my code doesn`t work properly:
#include <iostream>
#include <vector>
using namespace std;
bool remove(vector<int>& a, vector<int>& b){
for (size_t i = 0; i < a.size(); ++i )
{
while ( true )
{
std::vector<int>::iterator it;
it = std::find(b.begin(), b.end(), a[i]);
if ( it != b.end() )
{
i = 0; //because of moving elements
b.erase(b.begin()+i);
a.erase(a.begin()+i);
break;
}
}
}
return true;
}
int main(){
vector<int> a;
vector<int> b;
a.push_back(1);
a.push_back(2);
a.push_back(3);
b.push_back(1);
b.push_back(2);
b.push_back(3);
remove(a,b);
return 0;
}
The problem is - the last equal elements stay, so I can't remove them. How can I fix this, taking into consideration that unequal elements can be in the middle of the vector: a = 1, 2, 3; b = 1, 3, 3 -- 2 from vector a and 3 from vector b shouldn't be deleted?
As I understand, when I remove an element, other elements move.
That's partially true. If you remove an element, the elements after it get pushed down to fill the hole. If you remove the 5th element, the element that was the 6th element now becomes the 5th. But the element that was the 4th still stays the 4th. And this is certainly true only after you erase. So your code is:
i = // the element from a
it = // the iterator pointing to an element from b
i = 0;
b.erase(b.begin()+i); // erase the 1st element from b
a.erase(a.begin()+i); // erase the 1st element from a
You do all this work to find a pair of equal elements, and then immediately discard it - erasing the front element of both vectors. That's why it doesn't work. You want to erase the elements that you found:
b.erase(it);
a.erase(a.begin() + i); // no i = 0
Note that if you want to erase every element that has a match in the other array, this fix won't cut it - if you have two 1s in a but ony one in b you'd never remove the second one. For this, we need to take advantage of the Erase-remove idiom:
for (std::vector<int>::iterator itA = a.begin(); itA != a.end(); /* nothing */)
{
std::vector<int>::iterator itB = std::find(b.begin(), b.end(), *itA);
if (itB == b.end()) {
// done with this element, move on
++itA;
}
else {
// erase EVERYTHING
b.erase(std::remove(itB, b.end(), *itA), b.end());
itA = a.erase(std::remove(itA, a.end(), *itA), a.end());
}
}
Is it possible to change the size of a vector in C++11 while iterating over it? Clearly the iterator will be invalidated, but can the following clean syntax still be used?
std::vector<some_type> vec;
for(auto elem : vec) {
if(condition(elem)) {
new_elem = function(elem);
vec.insert(iterator_associated_with_elem+1, new_elem);
}
//Don't insert on condition(new_elem)
}
If not, what is the cleanest code to accomplish this task?
No, you can't. The standard mandates that the raged-based for behaves like a given algorithm. This algorithm uses iterators, which get invalidated when you modify the vector.
The simplest way for me is to to use iterators. Note that when we insert, we also reassign the iterator so that we always have a valid iterator:
auto it = vec.begin();
while(it < vec.end()) {
if (condition(*it)) {
new_elem = function(*it);
it = vec.insert(it + 1, new_elem);
}
++it;
}
No, you cannot use this trick, because there is an iterator behind your range loop. Once that iterator is invalidated, you cannot reference it again.
You can use this construct if you exit the loop immediately after the insertion. Otherwise, you need to use an index-based loop that starts at the back, and goes down to zero to avoid "seeing" elements that have been inserted during the execution of the loop.
std::vector<some_type> vec;
for(int i = vec.size()-1 ; i >= 0 ; i--) {
const some_type& elem(vec[i]);
if(condition(elem)) {
vec.insert(vec.begin()+i+1, function(elem));
}
//Don't insert on condition(new_elem)
}
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto &val : vec)
{
static int i = 0;
if (val == 5)
{
vec.insert(vec.begin() + i + 1, 6);
}
++i;
}
Values of vec will then be: 1 2 3 4 5 6
My question is very similar to How to find an item in a std::vector? However, I want to go further, suppose the item I am searching for appears several times in the vector, and I want to obtain its positions in the vector as well. For example, the vector I have is [ 1 3 4 3 7], and the item I want to search is 3. Then the positions of the item is 1 and 3. Using the std::find function, I can only obtain its first position in the vector. Any ideas?
Just stick it in a while loop,
auto i = std::find(v.begin(), v.end(), n);
std::vector<std::size_t> result;
while(i != v.end())
{
result.push_back(std::distance(v.begin(), i));
i = std::find(i + 1, v.end(), n);
}
Use std::find successive times, and then put all the results together. Use as first of the range in which you find, the position that the previous std::find returned to you plus one.
You can use std::find multiple times:
std::vector<int> vec;
// fill the vector with numbers
std::vector<int>::iterator it = vec.begin();
while (it != vec.end())
{
it = std::find(it, vec.end(), 3);
// do something with *it
if (it != vec.end())
it++;
}
Or you can simply use std::for_each:
std::vector<int> vec;
// fill the vector with numbers
std::for_each(vec.begin(), vec.end(), [&](int i)
{
if (i == 3)
{
// do something
}
});
If you are looking for the indexes/iterators for the items, you can simply use a custom loop:
std::vector<int> vec;
// fill the vector with numbers
std::vector<int> results;
for (std::size_t i = 0; i < vec.size(); ++i)
{
if (vec[i] == 3)
{
results.push_back(i);
}
}
results will then hold all of the indexes for elements matching your criteria (in this case, ==3).