So I am trying to append element from vector b into the end of vector a while erasing all the content in vector b. Below is my code, for some reason that erase is not working properly. Any input is appreciated Thx!!
void problem3(std::vector<int>& a, std::vector<int>& b){
typedef std::vector<int>::iterator iter;
int place_holder;
for (iter i = b.begin();i !=b.end();i++){
place_holder = *i;//use place hodler to store values temporairly
a.push_back(place_holder);//erase the elements from b
b.erase(i);
//std::cout<<b.size()<<'\n';
//append at the end of a
}
}
It's not a good idea to erase one element in the loop as the vector size is changing dynamically, which you will easily lose the correct index track.
Instead, try to erase all b's elements once in the end:
b.clear();
P.S.: There is one easier way to append a vector to another by using std::vector::insert() so that all you need is:
a.insert( a.end(), b.begin(), b.end() );
b.clear();
This is because your iterator becomes invalid on erase. erase will return you the iterator pointing to the next element, e.g. i = b.erase(i). Please be aware, that you want to avoid i++ (or better: ++i) in the for-loop, otherwise elements might be skipped.
Related
I am bit confused about the difference between the usage of std::remove algorithm. Specifically I am not able to understand what is being removed when I use this algorithm. I wrote a small test code like this:
std::vector<int> a;
a.push_back(1);
a.push_back(2);
std::remove(a.begin(), a.end(), 1);
int s = a.size();
std::vector<int>::iterator iter = a.begin();
std::vector<int>::iterator endIter = a.end();
std::cout<<"Using iter...\n";
for(; iter != endIter; ++iter)
{
std::cout<<*iter<<"\n";
}
std::cout<<"Using size...\n";
for(int i = 0; i < a.size(); ++i)
{
std::cout<<a[i]<<"\n";
}
The output was 2,2 in both the cases.
However, if I use erase with the remove something like this:
a.erase(std::remove(a.begin(), a.end(), 1), a.end());
I get the output as 2.
So my questions are:
(1). Is there any use of std::remove other than using it with erase function.
(2). Even after doing std::remove, why a.size() returns 2 and not 1?
I read the item in Scott Meyer's Effective STL book about the erase-remove idiom. But am still having this confusion.
remove() doesn't actually delete elements from the container -- it only shunts non-deleted elements forwards on top of deleted elements. The key is to realise that remove() is designed to work on not just a container but on any arbitrary forward iterator pair: that means it can't actually delete the elements, because an arbitrary iterator pair doesn't necessarily have the ability to delete elements.
For example, pointers to the beginning and end of a regular C array are forward iterators and as such can be used with remove():
int foo[100];
...
remove(foo, foo + 100, 42); // Remove all elements equal to 42
Here it's obvious that remove() cannot resize the array!
What does std::remove do?
Here's pseudo code of std::remove. Take few seconds to see what it's doing and then read the explanation.
Iter remove(Iter start, Iter end, T val) {
Iter destination = start;
//loop through entire list
while(start != end) {
//skip element(s) to be removed
if (*start == val) {
start++;
}
else //retain rest of the elements
*destination++ = *start++;
}
//return the new end of the list
return destination;
}
Notice that remove simply moved up the elements in the sequence, overwriting the values that you wanted to remove. So the values you wanted to remove are indeed gone, but then what's the problem? Let say you had vector with values {1, 2, 3, 4, 5}. After you call remove for val = 3, the vector now has {1, 2, 4, 5, 5}. That is, 4 and 5 got moved up so that 3 is gone from the vector but the size of vector hasn't changed. Also, the end of the vector now contains additional left over copy of 5.
What does vector::erase do?
std::erase takes start and end of the range you want to get rid off. It does not take the value you want to remove, only start and end of the range. Here's pseudo code for how it works:
erase(Iter first, Iter last)
{
//copy remaining elements from last
while (last != end())
*first++ = *last++;
//truncate vector
resize(first - begin());
}
So the erase operation actually changes the size of container and frees up the memory.
The remove-erase idiom
The combination of std::remove and std::erase allows you to remove matching elements from the container so that container would actually get truncated if elements were removed. Here's how to do it:
//first do the remove
auto removed = std::remove(vec.begin(), vec.end(), val);
//now truncate the vector
vec.erase(removed, vec.end());
This is known as the remove-erase idiom. Why is it designed like this? The insight is that the operation of finding elements is more generic and independent of underlying container (only dependent on iterators). However operation of erase depends on how container is storing memory (for example, you might have linked list instead of dynamic array). So STL expects containers to do its own erase while providing generic "remove" operation so all containers don't have to implement that code. In my view, the name is very misleading and std::remove should have been called std::find_move.
Note: Above code is strictly pseudocode. The actual STL implementation is more smarter, for example, using std::move instead of copy.
std::remove does not remove the actual objects, rather, pushes them to the end of the container. Actual deletion and deallocation of memory is done via erase. So:
(1). Is there any use of std::remove other than using it with erase function.
Yes, it helps to get a pair of iterators to a new sequence without having worry about proper de-allocation etc.
(2). Even after doing std::remove, why a.size() returns 2 and not 1?
The container still holds to those objects, you only have a new set of iterators to work with. Hence the size is still what it used to be.
i faced the same issue, trying to understand the difference.
the explanations that have been give so far are right on the money, but i only understood them after seeing an example;
#include <algorithm>
#include <string>
#include <iostream>
#include <cctype>
int main()
{
std::string str1 = "Text with some spaces";
std::string::iterator it = remove(str1.begin(), str1.end(), 't');
std::cout << str1 << std::endl;// prints "Tex wih some spaceses"
for (str1.begin();it != str1.end(); ++it)
{
std::cout << *it; //prints "es"
}
}
as you can see, the remove, only moves the lower case 't' to the end of the string, while returning a new iterator to the end of the new string (new string is the old string up to where the removed element are inserted)
this is why when you print the iterator that you got from "remove"
"Text with some spaces"
^ ^removes both 't', then shift all elements forward -1 //what we want to remove
"Text with some spaces"
^ end of string -2 //original state of string
"Tex with some spacess"
^end of string -3 //first 't' removed
"Tex wih some spaceses"
^end of string -4 //second 't' removed
"Tex wih some spaceses"
^new iterator that remove() returned -5 // the state of string after "remove" and without "erase"
if you pass the iterator you obtained from step 5 to "erase()" it will know to erase from there to the end of string re-sizing the string in process
To remove element with some condition(equal some value or other condition like less than) in container like vector, it always combine function member function erase and std::remove or std::remove_if.
In vector, the function erase can just delete element by position, like:
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
But if you want to erase elements with some condition, you can combine it with std::remove or std::remove_if.
For example, you want to erase all the elements 6 in the below vector:
std::vector<int> vec{6, 8, 10, 3, 4, 5, 6, 6, 6, 7, 8};
// std::remove move elements and return iterator for vector erase funtion
auto last = std::remove(vec.begin(), vec.end(), 6);
for(int a:vec)
cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8 6 6 7 8
vec.erase(last, vec.end());
for(int a:vec)
cout<<a<<" ";
cout<<endl;
// 8 10 3 4 5 7 8
std::remove works as below, it does't erase any elements, it just move elements and returns the iterator.
Possible implementation:
template< class ForwardIt, class T >
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value)
{
first = std::find(first, last, value);
if (first != last)
for(ForwardIt i = first; ++i != last; )
if (!(*i == value))
*first++ = std::move(*i);
return first;
}
Conclusion:
If you want to remove elements with some condition, you use vector::iterator erase (iterator first, iterator last); essentially.
First get range start:
auto last = std::remove(vec.begin(), vec.end(), equal_condition_value);
erase by range(always with end())
vec.erase(last, vec.end());
cited:
https://en.cppreference.com/w/cpp/algorithm/remove
Simplest I can come up with:
erase() is something you can do to an element in a container. Given an iterator/index into a container, erase( it ) removes the thing the iterator refers to from the container.
remove() is something you can do to a range, it re-arranges that range but doesn't
erase anything from the range.
remove doesn't "really" remove
anything, because it can't.
In order to "actually" remove the elements from container you need to access container APIs. Where as remove works only with iterators irrespective of what containers those iterators points to. Hence, even if remove wants an "actual remove", it can't.
Remove overwrite "removed" elements by the following elements that were not removed and then it is up to the caller to decide to use the returned new logical end instead of the original end.
In your case remove logically removed 1 from vector a but size remained to 2 itself. Erase actually deleted the elements from vector. [ from vector new end to old end ]
The main idea of remove is it cannot change the number of elements and it just remove elements from a range as per criteria.
I know ideally to add to a std::vector without worry I should use push_back(). However my problem set is that I need a clean code to check if the value I am entering is already in the std::vector and if not, I have to put in sequential, ascending order. To do that I am doing:
vector<Book>::iterator it;
it = std::find(books.begin(), books.end(), b);
if (it != books.end()) {
*it = b; // if b exists in books, overwrite the iterator
}
else {
vector<Book>::iterator _it;
_it = lower_bound(books.begin(), books.end(), b);
books.insert(_it, b); // here on an empty vector, _it has no values
}
The else will only run if the value b doesnt already exist in the std::vector. If this is the first value being checked against it, the else runs (since its empty) and the std::iterator is at books[0](?).
What makes me cautious about using this is that when debugging, on the insert() line, the value of _it reads "Error Reading...." for each of the members for which the std::iterator is pointing to. Now the program functions and yields anticipated results, but is it erroneously?
What you are doing works fine. However it is not the most efficient way. Using std::find doesn't take advantage of the fact that the data in the vector is sorted, it visits every element until if finds the correct one.
Instead of std::find you can use std::lower_bound from the beginning because that will find your element if it exists and if not, it will find the correct place to insert a new one.
Also it will use a binary search so it will be leaps and bounds faster than std::find. Also you don't end up finding the insertion/replacemt point twice.
Something like this should do:
void insert_or_replace(std::vector<Book>& books, Book const& b)
{
std::vector<Book>::iterator it;
it = std::lower_bound(books.begin(), books.end(), b);
if(it != books.end() && *it == b)
*it = b;
else
books.insert(it, b);
}
It is all well defined behavior.
lower_bound will return the end iterator (books.end() in your case) for an empty range or if b should come after the last element. insert will add the new element before the iterator passed to it, so this will be before end. In an empty vector, this will have the effect of adding the element to the vector.
The code is pretty fine. For an empty vector lower_bound(books.begin(), books.end(), b) will return end() iterator. Passing it to std::vector::insert will work fine.
pos - iterator before which the content will be inserted. pos may be the end() iterator
and about your worry:
What makes me cautious about using this is that when debugging, on the insert() line, the value of _it reads "Error Reading...." for each of the members for which the std::iterator is pointing to.
That's because the end() iterator points to the position following the last element, so it's invalid to deference on it (to get a nonexistent element).
I have a vector like below
vector<unsigned int> myvector;
vector<unsigned int>::iterator it;
//fill it
for (i=1; i<=10; i++) myvector.push_back(i);
//I want delete first one
it = myvector.begin();
myvector.erase(it++);
//I want to know if I am in last element
if(it != myvector.end())
cout << "a test";
but in compare expression my program crashes. why?
Simply put, because after calling erase(), all iterators that point to the erased position or later positions in the vector are invalid. See, for example, here for official documentation of this.
Because erase() invalidates the iterators at the and after the point of erasure.
OTOH, the return value is an iterator that points at the element after the erased one, so you can do it like this:
it = myvector.erase(it);
if (it == myvector.end()) { ... }
you should not ha to increment 'i' while erasing it since erase return the next valid iterator
I think that this probably answer your question
https://stackoverflow.com/a/4645727/623546
The iterators between the position of the erased element and the end of the vector are invalidated by erase(). See here.
I am looping through a vector with a loop such as for(int i = 0; i < vec.size(); i++). Within this loop, I check a condition on the element at that vector index, and if a certain condition is true, I want to delete that element.
How do I delete a vector element while looping over it without crashing?
The idiomatic way to remove all elements from an STL container which satisfy a given predicate is to use the remove-erase idiom. The idea is to move the predicate (that's the function which yields true or false for some element) into a given function, say pred and then:
static bool pred( const std::string &s ) {
// ...
}
std::vector<std::string> v;
v.erase( std::remove_if( v.begin(), v.end(), pred ), v.end() );
If you insist on using indices, you should not increment the index for every element, but only for those which didn't get removed:
std::vector<std::string>::size_type i = 0;
while ( i < v.size() ) {
if ( shouldBeRemoved( v[i] ) ) {
v.erase( v.begin() + i );
} else {
++i;
}
}
However, this is not only more code and less idiomatic (read: C++ programmers actually have to look at the code whereas the 'erase & remove' idiom immediately gives some idea what's going on), but also much less efficient because vectors store their elements in one contiguous block of memory, so erasing on positions other than the vector end also moves all the elements after the segment erased to their new positions.
If you cannot use remove/erase (e.g. because you don't want to use lambdas or write a predicate), use the standard idiom for sequence container element removal:
for (auto it = v.cbegin(); it != v.cend() /* not hoisted */; /* no increment */)
{
if (delete_condition)
{
it = v.erase(it);
}
else
{
++it;
}
}
If possible, though, prefer remove/erase:
#include <algorithm>
v.erase(std::remove_if(v.begin(), v.end(),
[](T const & x) -> bool { /* decide */ }),
v.end());
Use the Erase-Remove Idiom, using remove_if with a predicate to specify your condition.
if(vector_name.empty() == false) {
for(int i = vector_name.size() - 1; i >= 0; i--)
{
if(condition)
vector_name.erase(vector_name.at(i));
}
}
This works for me. And Don't need to think about indexes have already erased.
Iterate over the vector backwards. That way, you don't nuke the ability to get to the elements you haven't visited yet.
I realize you are asking specifically about removing from vector, but just wanted to point out that it is costly to remove items from a std::vector since all items after the removed item must be copied to new location. If you are going to remove items from the container you should use a std::list. The std::list::erase(item) method even returns the iterator pointing to the value after the one just erased, so it's easy to use in a for or while loop. Nice thing too with std::list is that iterators pointing to non-erased items remain valid throughout list existence. See for instance the docs at cplusplus.com.
That said, if you have no choice, a trick that can work is simply to create a new empty vector and add items to it from the first vector, then use std::swap(oldVec, newVec), which is very efficient (no copy, just changes internal pointers).
While using the vector why do we sometime use the operator[] like homework[mid] but other times use homework.begin(). Also, homework.end() not homework[end], but that's like begin. Is it just accessing the elements differently? It's more confusing this way, don't you agree?
vector::operator[] retrieves the Nth element of the vector. Such an operator is defined only for select STL container classes.
vector.end() is a method returning an iterator. Iterators are special entities for working with STL containers, vector included. vector::end() points onto the element immediately following the last element of the vector - it's often treated as a value to campare the iterator against to determine whether the whole container has been traversed.
It's the same analogy as with C arrays...
int a[length_a]
"begin/end" version:
int* begin = a;
int* end = a+length_a;
while (p<e) {
printf("%d", *p);
++p;
}
Note how "end" points one element beyond the array! It's exactly same as vector::end (think about it as a stop condition).
"[]" version:
for (int i=0; i<length_a; ++i) {
printf("%d", a[i]);
}
'[]' summary:
+compatible with C arrays, efficient syntax
-not available for non-random access containers ( for example: changing to "list" will be difficult)
my POV advice:
use [] for random access or as an 'easy' upgrade to c array
use begin/end when traversing whole content sequentially
begin() and end() are member function of vector which return an iterator. This iterator can be dereferenced to get the value. Similarly, you have operator[] which takes an index and directly returns the value stored at that position.
They do different things. operator[n] returns a reference a reference to the nth element.
begin() and end() returns iterators pointing to the beginning and end of the container. If you need to iterate over the container, then using iterators is hardly "more confusing". It's much much simpler.
Let's say you have a vector v:
std::vector<int> v;
v.push_back(1);
v.push_back(7);
v.push_back(42);
v.push_back(3);
if you want to simply reference an element, you have these options:
v[2];
*v.begin()+2;
So here, operator[] is simpler.
But if you want to iterate over the container, perhaps to print out the contents, you have these options:
for (int i = 0; i < v.size(); ++i){
std::cout << v[i];
}
std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(std::cout));
Suddenly, the iterators saved us a lot of work. We no longer have to make a loop.
Or let's say you wanted to find the largest element:
int max = 0;
for (int i = 0; i < v.size(); ++i){
if (v[i] > max) { max = v[i]; }
}
std::max_element(v.begin(), v.end());
or maybe you want to sort the vector. I won't even bother writing out my own sorting algorithm using operator[], but here's the iterator version:
std::sort(v.begin(), v.end());
That's why begin(), end() are useful.