Remove object from vector based on object property [duplicate] - c++

This question already has answers here:
Using erase-remove_if idiom
(3 answers)
Closed 4 years ago.
I want to delete object from vector which has property set on specific value. In this example:
void RemoveUser(int val)
{
users.remove_if([val](User u)
{
return u.val == val;
});
}
I can remove specific user based on its val property. This works on std::list, but its not working on std::vector. How can I archieve same result on vector?

std::vector doesn't have a remove_if member. But there is a standard algorith, aptly named std::remove_if that can work in tandem with the vectors erase member function.
users.erase(remove_if(begin(users), end(users), [val](User const& u)
{
return u.val == val;
}), end(users));
First remove_if shifts the elements of the range, and returns an iterator past the end of all the "good ones". The vector is of the same size as when we started at this point. Now that iterator is fed to erase, which kills all of those items, from that "new end", to the "old end" of the vector.
The reason std::list implements its own member version of remove_if is to better utilize its node based structure. It doesn't have to do copies to shift elements around or delete them. For std::vector, the algorithm is the best we can do.

Related

C++ copy vector range before erase [duplicate]

This question already has answers here:
Move all elements which satisfy some condition from one container to another, i.e. I'm looking for some kind of "move_if"
(3 answers)
Closed 4 years ago.
I have
std::vector v_1;
std::vector v_2;
and I want to delete some elements in v_1 but store them in vector v_2 before doing so. Something along the lines
auto deleteIf = [](auto& x) {return x.checkDelete();}
auto it = std::remove_if(v_1.begin(), v_1.end(), deleteIf);
// how can I efficiently copy elements it to v_1 end to v2?
v1.erase(it, v_2.end());
How can I most efficiently copy the elements that will be erased to v_2?
You cannot rely on remove_if to keep your elements intact. From cppreference.com :
[...] 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 [...]
You have to somehow move them.
You can partition your vector and than split it into two:
std::vector<type> v_1;
std::vector<type> v_2;
auto split_check = [](auto&) { x.checkDelete() };
// stable_partition will keep the order. if not required use std::partition
auto it = std::stable_partition(v_1.begin(), v_1.end(), split_check);
v_2.insert(v2.end(), it, v_1.end());
v_1.erase(it, v_1.end());
running example.
I just realized the duplicate answer from T.C. mentions the exact same thing... ¯\_(ツ)_/¯

Is it possible to send part of vector as a vector to a function? [duplicate]

This question already has answers here:
How can I pass a part of a vector as a function argument?
(7 answers)
Closed 5 years ago.
I want to see if it is possible to pass part of vector to a function so that it appears as a normal vector to the function. More importantly, I want this to be done in O(1), constant time. I don't want to iterate the vector to make a new one. In fact, I also want the size of the new vector to change to 40 in the following example.
void func(vector <int> &v){
//calling index 10 to 50 of v
func(v[10..50])
}
Use iterators as the parameters to the range-based function, and pass in the required range. Your code in the function become
funcWithRange(v.cbegin()+10, v.cbegin()+50);
with function signature
void funcWithRange(std::vector<int>::const_iterator first, std::vector<int>::const_iterator last)
This could be generalized by making this a function template with the vector member type as its template parameter, or still further to any container supporting this type of range iteration. As noted in the comments, <algorithm> has many examples of this pattern.
std::distance(first, last); will return the desired altered size. I don't think you can get closer to meeting your requirements without making a physical copy.
If you have a vector of say 100 elements
std::vector<int> v(100);
and you want to call void f(std::vector<int> v) with the first 10 elements, simply call the function as
f({v.cbegin(), v.cbegin() + 10});
That will construct a new vector from the two iterators (by copying the elements) and pass the new vector to f.
There is a means to achieve something similar to this that is being proposed for inclusion in the C++ standard. It is called a span and it operates like a vector in some ways.
#include <gsl/span>
void func(gsl::span<int> sp)
{
for(auto& i: sp)
std::cout << i << '\n';
}
int main()
{
// ...
std::vector<int> v(100);
// put something in the vector (numbers 0 - 99)
std::iota(std::begin(v), std::end(v), 0);
// wrap the container in a span
auto sp = gsl::make_span(v);
// send parts of it to functions
func(sp.subspan(10, 50));
}
The span presents a window onto the original vector so it is a reference type. It doesn't contain its own data just pointers to the data in the vector. As such they are lightweight and designed to be passed by value.
An implementation of span can be found here: https://github.com/Microsoft/GSL
This is the recommended way of passing contiguous containers in the Best Practices guide by Bjarne Stroustrup and Herb Sutter.
The guidelines can be found here: CppCoreGuidelines.md

getting "index" of set element via iterator [duplicate]

This question already has answers here:
index or position in std::set
(3 answers)
Closed 6 years ago.
This question applies to both std::set and std::unsorted_set.
I have an iterator to an element in a set. I'd like to use the iterator to get an "index" for the element based on its location in the set.
For example, the indices for my set would be as follows:
int index = 0;
for(MySetType::iterator begin = mySet.begin(); begin != mySet.end(); begin++)
{
cout << "The index for this element is " << index;
index++;
}
I have tried doing arithmetic using iterators but it doesn't work:
int index = mySetIterator - mySet.begin();
Is there any way to use the iterator to get an index value like this based on its location in the set?
Use STL distance, namely std::distance(set.begin(), mySetIterator)
Please note that:
Returns the number of elements between first and last. The behavior
is undefined if last is not reachable from first by (possibly
repeatedly) incrementing first.
Remark : Complexity is linear;
However, if InputIt additionally meets the requirements of
LegacyRandomAccessIterator, complexity is constant.
std::set and set::unordered_set are associative containers, not sequence containers, hence the concept itself of index doesn't make much sense.
If you need to retrieve an index for an associative container then design should be changed (even because without a concept of least or most recent inserted element the indices in such containers are subject to change).
std::set has just a bidirectional iterator, which means you can't do what you're trying to do with operator + (or -). Those are only available to random access iterators, like std::vector provides.
You need to use std::distance to get the "index", and std::advance to move from the beginning of the set to the end.
auto distance = std::distance(mySet.begin(), someIterator);
auto it = mySet.begin();
std::advance(it, distance);
assert(it == someIterator);

How to traverse vector from end to start? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Erasing elements from a vector
I want to delete some elements as I go and so dont want miss the iteration
I travers right now as follows
vector<double> distances;
for(size_t i=0; i<distances.size();i++)
{
}
How do I traverse from end to beginning so I can safely delete elements and yet access the element I want?
for (size_t i = distances.size() - 1; i >=0; --i)
However, you should use an std::remove_if instead
The best way to handle this is to cheat, actually.
In C++, we have the erase/remove idiom to have the most efficiency. You create a predicate and then you're on, and it's gotten quite simple with lambdas now.
// This removes all odd elements in vec
vec.erase(vec.remove_if([](int i) { return i % 2 == 1 }), vec.end();
The way remove_if works is that it shuffles elements around (either copying or moving) so as to gather all the elements you wished to keep at the head of the container, the tail being left in an unspecified (but valid) state, and then returns to you an iterator to the first element of the tail.
By then using the range-erase method of the container, you ask the container to remove all those elements in one go.
use the reverse_iterator :
vector<something>::reverse_iterator iter = v.rbegin();
iter++; //Iterates backwards

C++ std::list: Erasing / removing elements while iterating [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Can you remove elements from a std::list while iterating through it?
I have a loop in a function, that iterates over an std::list from begin to end.
In each cycle, I perform some checks and maybe do a few manipulations on the current list entry, and in some cases I want to remove it from the list.
Now, as expected my iterator gets invalidated.
Is there any way to work around this, to remove elements from a list while iterating over it?
Catch the return value of erase and use it as your iterator. The return value is an iterator to the next valid location after the erasure.
if(ShouldErase)
{
iter = list.erase(iter);
}
else
{
++iter;
}
Reference
Excerpt:
Return value
A bidirectional iterator pointing to the new location of the element that followed the last element erased by the function call, which is the list end if the operation erased the last element in the sequence.
Use postfix increment.
list.erase(it++);
it is increased, so it no longer refers to the erased element, then the previous value of it is given to list.erase. Make sure that you either do list.erase(it++) or ++it in your loop - doing both will skip elements and potentially increment past end of the list.
Have you considered using the list::remove_if algorithm?