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
Related
I have a below program. I need to understand why do we need to decrement pointer when we use erase method?
Also is there any better way which does not create a such confusion ?
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> myvector{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (auto i = myvector.begin(); i != myvector.end(); ++i) {
if (*i % 2 == 0) {
myvector.erase(i);
i--;// why do we need to decrement ?
}
}
// Printing the vector
for (auto it = myvector.begin(); it != myvector.end(); ++it)
cout << ' ' << *it;
return 0;
}
I need to understand why do we need to decrement pointer when we use erase method?
You don't, and shouldn't. The correct thing to do is use the return value of erase. At the moment your program has undefined behaviour because you are modifying an invalid iterator.
for (auto i = myvector.begin(); i != myvector.end();) {
if (*i % 2 == 0) {
i = myvector.erase(i);
} else {
++i;
}
}
Also is there any better way which does not create a such confusion ?
Yes, std::remove_if
auto is_even = [](int i){ return (i % 2) == 0; };
auto last = std::remove_if(myvector.begin(), myvector.end(), is_even);
myvector.erase(last, myvector.end());
You have a good answer from Caleth, but I'm going to add some information:
for (auto i = myvector.begin(); i != myvector.end(); ++i) {
if (*i % 2 == 0) {
myvector.erase(i);
i--;// why do we need to decrement ?
}
}
That's your code. For my explanation, let's believe that the iterator is just an index (an int). It's not, but it works for understanding.
Let's say myvector contains the integers 0..10 and you delete 4. Once you do:
0 1 2 3 5 6 7 8 9 10
Now, imagine your loop. You get to the bottom of the for-loop and increment i -- it now has 5, but you skip over checking if the old myvector[5] -- which is now stored in myvector[4] -- fits your if-statement.
The other way to write this:
for (auto i = myvector.begin(); i != myvector.end(); ) {
if (...) ...
else {
++i;
}
}
That is, you only increment the iterator if you do NOT delete. This avoids skipping over the item after the one you deleted (because it moved into the deleted guy's spot).
However, this is the wrong way to do it, and Caleth has better answers for how to do it properly.
vector<int> cutTheSticks(vector<int> arr) {
vector<int> res;
vector<int>::iterator it;
int i=0, mini=0, c=arr.size();
while(c>0) {
res.push_back(c);
mini=*min_element(arr.begin(),arr.end());
for(i=0;i<arr.size();i++) {
arr[i]-=mini;
}
for(auto it=arr.begin();it!=arr.end();it++) {
i=*it;
if(i==0)
arr.erase(it);
}
c=arr.size();
}
return res;
}
I am running this piece of code in the hackerank portal and not on any system.
The way you are using erase is causing the problem in this case. In fact, you exactly don't need a complex approach like this for the problem.
You can simply sort the array in reverse order and then use pop_back() while last element is 0. It will also help to reduce complexity as then you won't need to call min_element each time. You can directly use arr.back() for the minimum element.
Logic behind my approach:
In each iteration, you are subtracting minimum element from each element. This makes number(s) having the minimum value as 0. Clearly, since the array is sorted in reverse order, these numbers will be in the end of the array. You then want to remove these elements for which pop_back is one of the best available options.
Here is sample code:
vector<int> cutTheSticks(vector<int> arr) {
vector<int> res;
sort(arr.begin(), arr.end(), greater<int>());
while (not arr.empty()) {
res.push_back(arr.size());
for (auto &&i : arr)
i -= arr.back();
while (not (arr.back() or arr.empty()))
arr.pop_back();
}
return res;
}
PS:
If you want to stick with your original algorithm then replace
for (auto it = arr.begin(); it!=arr.end(); it++) {
i = *it;
if(i == 0)
arr.erase(it);
}
with something like:
arr.erase(remove(arr.begin(), arr.end(), 0), arr.end()); // called as erase-remove idiom
or
for (auto it = arr.begin(); it!=arr.end(); /* it++ */) {
i = *it;
if(i == 0)
it = arr.erase(it);
else
++it;
}
This thread may help you: Remove elements of a vector inside the loop.
I am working on a problem where I have to create subvectors from a bigger vector. If the elements in the vector are consecutive I have to create a vector of those elements. If there are elements which are not consecutive then a vector of that single elements is created. My logic is as below
vector<int> vect;
for (int nCount=0; nCount < 3; nCount++)
vect.push_back(nCount);
vect.push_back(5);
vect.push_back(8);
vector<int>::iterator itEnd;
itEnd = std::adjacent_find (vect.begin(), vect.end(), NotConsecutive());
The functor NotConsecutiveis as below
return (int first != int second-1);
So I am expecting the std::adjacent_find will give me back the iterators such that I can create vector one{0,1,2,3}, vector two{5} and vector{8}. But I am not sure if there is any simpler way?
Edit:I forgot to mention that I have std::adjacent_find in a loop as
while(itBegin != vect.end())
{
itEnd = std::adjacent_find (vect.begin(), vect.end(), NotConsecutive());
vector<int> groupe;
if( std::distance(itBegin, itEnd) < 1)
{
groupe.assign(itBegin, itBegin+1);
}
else
{
groupe.assign(itBegin, itEnd);
}
if(boost::next(itEnd) != vect.end())
{
itBegin = ++itEnd;
}
else
{
vector<int> last_element.push_back(itEnd);
}
}
Does it make any sense?
I think this is what is being requested. It does not use adjacent_find() but manually iterates through the vector populating a vector<vector<int>> containing the extracted sub-vectors. It is pretty simple, IMO.
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
std::vector<int> vect { 0, 1, 2, 3, 5, 8 };
// List of subvectors extracted from 'vect'.
// Initially populated with a single vector containing
// the first element from 'vect'.
//
std::vector<std::vector<int>> sub_vectors(1, std::vector<int>(1, vect[0]));
// Iterate over the elements of 'vect',
// skipping the first as it has already been processed.
//
std::for_each(vect.begin() + 1,
vect.end(),
[&](int i)
{
// It the current int is one more than previous
// append to current sub vector.
if (sub_vectors.back().back() == i - 1)
{
sub_vectors.back().push_back(i);
}
// Otherwise, create a new subvector contain
// a single element.
else
{
sub_vectors.push_back(std::vector<int>(1, i));
}
});
for (auto const& v: sub_vectors)
{
for (auto i: v) std::cout << i << ", ";
std::cout << std::endl;
}
}
Output:
0, 1, 2, 3,
5,
8,
See demo at http://ideone.com/ZM9ssk.
Due to the limitations of std::adjacent_find you can't use it quite the way you want to. However it can still be useful.
What you can do is to iterate over the collection, and use std::adjacent_find in a loop, with the last returned iterator (or your outer loop iterator for the first call) until it returns end. Then you will have a complete set of consecutive elements. Then continue the outer loop from where the last call to std::adjacent_find returned a non-end iterator.
Honestly, I don't find any clear disadvantage of using a simple hand-crafted loop instead of standard functions:
void split(const std::vector<int> &origin, vector<vector<int> > &result)
{
result.clear();
if(origin.empty()) return;
result.resize(1);
result[0].push_back(origin[0]);
for(size_t i = 1; i < origin.size(); ++i)
{
if(origin[i] != origin[i-1] + 1) result.push_back(vector<int>());
result.back().push_back(origin[i]);
}
}
If I'm using an iterator in a for loop and I use erase on a current iteration of iterator, the for loop should continue fine and access the rest of the list elements?
From what I have read, this should be the case and is a primary distinguishing characteristic of list vs deque or vector. For my purposes, a queue might work but I need this behavior.
Here is the loop I am considering:
std::list<Sequence>::iterator iterator;
iterator=m_concurrents.begin();
for (;iterator!=m_concurrents.end();++iterator){
if (iterator->passes()){
m_concurrents.erase(iterator);
}
}
The idiomatic way to write that loop would be:
for (auto i = list.begin(); i != list.end();) {
if (condition)
i = list.erase(i);
else
++i;
}
You can do the same thing with a set, multiset, map, or multimap. For these containers you can erase an element without affecting the validity to any iterators to other elements. Other containers like vector or deque are not so kind. For those containers only elements before the erased iterator remain untouched. This difference is simply because lists store elements in individually allocated nodes. It's easy to take one link out. vectors are contiguous, taking one element out moves all elements after it back one position.
Your loop is broken because you erase the element at i on some given condition. i is no longer a valid iterator after that call. Your for loop then increments i, but i is not valid. Hell upon earth ensues. This is the exact situation that is why erase returns the iterator to the element after what was erased... so you can continue traversing the list.
You could also use list::remove_if:
list.remove_if([](auto& i) { return i > 10; });
In the lambda, return true if the element should be removed. In this example, it would remove all elements greater than 10.
for (auto i = list.begin(); i != list.end(); ++i) {
if (condition) {
list.erase(i);
--i;
}
}
If you just want to use for iterator, you can use it this way, for example:
list<int> lst{4, 1, 2, 3, 5};
for(auto it = lst.begin(); it != lst.end();++it){
if ((*it % 2) == 1){
it = lst.erase(it); erase and go to next(erase will return the next iterator)
--it; // as it will be add again in for, so we go back one step
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
But erase in while iterator will be more clear:
list<int> lst{4, 1, 2, 3, 5};
auto it = lst.begin();
while (it != lst.end()){
if((*it % 2) == 1){
it = lst.erase(it);// erase and go to next
} else{
++it; // go to next
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
You can also use member function remove_if:
list<int> lst{4, 1, 2, 3, 5};
lst.remove_if([](int a){return a % 2 == 1;});
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
Or use std::remove_if conbine with erase funtion:
list<int> lst{4, 1, 2, 3, 5};
lst.erase(std::remove_if(lst.begin(), lst.end(), [](int a){
return a % 2 == 1;
}), lst.end());
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
You can also reference to this question:
Removing item from vector, while in C++11 range 'for' loop?
The goal is to access the "nth" element of a vector of strings instead of the [] operator or the "at" method. From what I understand, iterators can be used to navigate through containers, but I've never used iterators before, and what I'm reading is confusing.
If anyone could give me some information on how to achieve this, I would appreciate it. Thank you.
You need to make use of the begin and end method of the vector class, which return the iterator referring to the first and the last element respectively.
using namespace std;
vector<string> myvector; // a vector of stings.
// push some strings in the vector.
myvector.push_back("a");
myvector.push_back("b");
myvector.push_back("c");
myvector.push_back("d");
vector<string>::iterator it; // declare an iterator to a vector of strings
int n = 3; // nth element to be found.
int i = 0; // counter.
// now start at from the beginning
// and keep iterating over the element till you find
// nth element...or reach the end of vector.
for(it = myvector.begin(); it != myvector.end(); it++,i++ ) {
// found nth element..print and break.
if(i == n) {
cout<< *it << endl; // prints d.
break;
}
}
// other easier ways of doing the same.
// using operator[]
cout<<myvector[n]<<endl; // prints d.
// using the at method
cout << myvector.at(n) << endl; // prints d.
In C++-11 you can do:
std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto i : v)
{
// access by value, the type of i is int
std::cout << i << ' ';
}
std::cout << '\n';
See here for variations: https://en.cppreference.com/w/cpp/language/range-for
Typically, iterators are used to access elements of a container in linear fashion; however, with "random access iterators", it is possible to access any element in the same fashion as operator[].
To access arbitrary elements in a vector vec, you can use the following:
vec.begin() // 1st
vec.begin()+1 // 2nd
// ...
vec.begin()+(i-1) // ith
// ...
vec.begin()+(vec.size()-1) // last
The following is an example of a typical access pattern (earlier versions of C++):
int sum = 0;
using Iter = std::vector<int>::const_iterator;
for (Iter it = vec.begin(); it!=vec.end(); ++it) {
sum += *it;
}
The advantage of using iterator is that you can apply the same pattern with other containers:
sum = 0;
for (Iter it = lst.begin(); it!=lst.end(); ++it) {
sum += *it;
}
For this reason, it is really easy to create template code that will work the same regardless of the container type.
Another advantage of iterators is that it doesn't assume the data is resident in memory; for example, one could create a forward iterator that can read data from an input stream, or that simply generates data on the fly (e.g. a range or random number generator).
Another option using std::for_each and lambdas:
sum = 0;
std::for_each(vec.begin(), vec.end(), [&sum](int i) { sum += i; });
Since C++11 you can use auto to avoid specifying a very long, complicated type name of the iterator as seen before (or even more complex):
sum = 0;
for (auto it = vec.begin(); it!=vec.end(); ++it) {
sum += *it;
}
And, in addition, there is a simpler for-each variant:
sum = 0;
for (auto value : vec) {
sum += value;
}
And finally there is also std::accumulate where you have to be careful whether you are adding integer or floating point numbers.
Vector's iterators are random access iterators which means they look and feel like plain pointers.
You can access the nth element by adding n to the iterator returned from the container's begin() method, or you can use operator [].
std::vector<int> vec(10);
std::vector<int>::iterator it = vec.begin();
int sixth = *(it + 5);
int third = *(2 + it);
int second = it[1];
Alternatively you can use the advance function which works with all kinds of iterators. (You'd have to consider whether you really want to perform "random access" with non-random-access iterators, since that might be an expensive thing to do.)
std::vector<int> vec(10);
std::vector<int>::iterator it = vec.begin();
std::advance(it, 5);
int sixth = *it;
Here is an example of accessing the ith index of a std::vector using an std::iterator within a loop which does not require incrementing two iterators.
std::vector<std::string> strs = {"sigma" "alpha", "beta", "rho", "nova"};
int nth = 2;
std::vector<std::string>::iterator it;
for(it = strs.begin(); it != strs.end(); it++) {
int ith = it - strs.begin();
if(ith == nth) {
printf("Iterator within a for-loop: strs[%d] = %s\n", ith, (*it).c_str());
}
}
Without a for-loop
it = strs.begin() + nth;
printf("Iterator without a for-loop: strs[%d] = %s\n", nth, (*it).c_str());
and using at method:
printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());