How to traverse vector from end to start? [duplicate] - c++

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

Related

Usage of for range in Vectors in order to add elements

A lot of questions related to for range has been asked around here, but I cannot find the version I need.
So, I was reading this book called C Primer 5th edition, and while reading about Vectors I read a line which stated,
we cannot use a range for if the body of the loop adds elements to the vector.
But just before this line there was a code part written to add elements in the vectors during the run time, i.e.,
vector<int> v2; // empty vector
for (int i = 0; i != 100; ++i)
v2.push_back(i); // append sequential integers to v2
// at end of loop v2 has 100 elements, values 0 . . . 99
All I want to ask is that how can they both contradict to one another? Or are they completely different and not related to one another? If the second one is the condition can you explain me how this works?
And if the former is correct can you please explain me why do the code part and the statement contradicted to one another?
The reason that (amongst other things) a range-based for loop shouldn't append elements is because behind the curtains, a range-based for loop is just a loop that iterates over the container's iterators from begin to end. And std::vector::push_back will invalidate all iterators:
If the new size() is greater than capacity() then all iterators and
references (including the past-the-end iterator) are invalidated.
Otherwise only the past-the-end iterator is invalidated.
Contrary to using iterators, however, the same cannot be said when indexing the vector, because even if push_back invalidats iterators, vec[3] will always refer to the element at position 3 (assuming size() > 3).
Furthermore, the loop you show does not even iterate over the vector's elements. It just adds more elements to an existing vector, and it is not a range-based for loop.
To be clear, a construct that would be a problem would be the following:
std::vector<int> vec(10); // 10 times a zero
vec[0] = 1;
for (int k: vec)
if (k == 1)
vec.push_back(2); // invalidates iterators!
While this, equivalent looking code, is legal:
std::vector<int> vec(10); // 10 times a zero
vec[0] = 1;
for (std::size_t i = 0; i < std::size(vec); ++i)
if (vec[i] == 1)
vec.push_back(2);

How to avoid out of range exception when erasing vector in a loop?

My apologies for the lengthy explanation.
I am working on a C++ application that loads two files into two 2D string vectors, rearranges those vectors, builds another 2D string vector, and outputs it all in a report. The first element of the two vectors is a code that identifies the owner of the item and the item in the vector. I pass the owner's identification to the program on start and loop through the two vectors in a nested while loop to find those that have matching first elements. When I do, I build a third vector with components of the first two, and I then need to capture any that don't match.
I was using the syntax "vector.erase(vector.begin() + i)" to remove elements from the two original arrays when they matched. When the loop completed, I had my new third vector, and I was left with two vectors that only had elements, which didn't match and that is what I needed. This was working fine as I tried the various owners in the files (the program accepts one owner at a time). Then I tried one that generated an out of range error.
I could not figure out how to do the erase inside of the loop without throwing the error (it didn't seem that swap and pop or erase-remove were feasible solutions). I solved my problem for the program with two extra nested while loops after building my third vector in this one.
I'd like to know how to make the erase method work here (as it seems a simpler solution) or at least how to check for my out of range error (and avoid it). There were a lot of "rows" for this particular owner; so debugging was tedious. Before giving up and going on to the nested while solution, I determined that the second erase was throwing the error. How can I make this work, or are my nested whiles after the fact, the best I can do? Here is the code:
i = 0;
while (i < AIvector.size())
{
CHECK:
j = 0;
while (j < TRvector.size())
{
if (AIvector[i][0] == TRvector[j][0])
{
linevector.clear();
// Add the necessary data from both vectors to Combo_outputvector
for (x = 0; x < AIvector[i].size(); x++)
{
linevector.push_back(AIvector[i][x]); // add AI info
}
for (x = 3; x < TRvector[j].size(); x++) // Don't need the the first three elements; so start with x=3.
{
linevector.push_back(TRvector[j][x]); // add TR info
}
Combo_outputvector.push_back(linevector); // build the combo vector
// then erase these two current rows/elements from their respective vectors, this revises the AI and TR vectors
AIvector.erase(AIvector.begin() + i);
TRvector.erase(TRvector.begin() + j);
goto CHECK; // jump from here because the erase will have changed the two increments
}
j++;
}
i++;
}
As already discussed, your goto jumps to the wrong position. Simply moving it out of the first while loop should solve your problems. But can we do better?
Erasing from a vector can be done cleanly with std::remove and std::erase for cheap-to-move objects, which vector and string both are. After some thought, however, I believe this isn't the best solution for you because you need a function that does more than just check if a certain row exists in both containers and that is not easily expressed with the erase-remove idiom.
Retaining the current structure, then, we can use iterators for the loop condition. We have a lot to gain from this, because std::vector::erase returns an iterator to the next valid element after the erased one. Not to mention that it takes an iterator anyway. Conditionally erasing elements in a vector becomes as simple as
auto it = vec.begin()
while (it != vec.end()) {
if (...)
it = vec.erase(it);
else
++it;
}
Because we assign erase's return value to it we don't have to worry about iterator invalidation. If we erase the last element, it returns vec.end() so that doesn't need special handling.
Your second loop can be removed altogether. The C++ standard defines functions for searching inside STL containers. std::find_if searches for a value in a container that satisfies a condition and returns an iterator to it, or end() if it doesn't exist. You haven't declared your types anywhere so I'm just going to assume the rows are std::vector<std::string>>.
using row_t = std::vector<std::string>;
auto AI_it = AIVector.begin();
while (AI_it != AIVector.end()) {
// Find a row in TRVector with the same first element as *AI_it
auto TR_it = std::find_if (TRVector.begin(), TRVector.end(), [&AI_it](const row_t& row) {
return row[0] == (*AI_it)[0];
});
// If a matching row was found
if (TR_it != TRVector.end()) {
// Copy the line from AIVector
auto linevector = *AI_it;
// Do NOT do this if you don't guarantee size > 3
assert(TR_it->size() >= 3);
std::copy(TR_it->begin() + 3, TR_it->end(),
std::back_inserter(linevector));
Combo_outputvector.emplace_back(std::move(linevector));
AI_it = AIVector.erase(AI_it);
TRVector.erase(TR_it);
}
else
++AI_it;
}
As you can see, switching to iterators completely sidesteps your initial problem of figuring out how not to access invalid indices. If you don't understand the syntax of the arguments for find_if search for the term lambda. It is beyond the scope if this answer to explain what they are.
A few notable changes:
linevector is now encapsulated properly. There is no reason for it to be declared outside this scope and reused.
linevector simply copies the desired row from AIVector rather than push_back every element in it, as long as Combo_outputvector (and therefore linevector) contains the same type than AIVector and TRVector.
std::copy is used instead of a for loop. Apart from being slightly shorter, it is also more generic, meaning you could change your container type to anything that supports random access iterators and inserting at the back, and the copy would still work.
linevector is moved into Combo_outputvector. This can be a huge performance optimization if your vectors are large!
It is possible that you used an non-encapsulated linevector because you wanted to keep a copy of the last inserted row outside of the loop. That would prohibit moving it, however. For this reason it is faster and more descriptive to do it as I showed above and then simply do the following after the loop.
auto linevector = Combo_outputvector.back();

Only Move Iterator Once

I'm trying to find a way to improve my answer here. Let's simplify the question to say: I want to partition the input container, lets call it foo, into a vector of vectors of size STEP, the last of these vectors shall have a smaller size if there were less than STEP elements remaining in the input container, let's call this partitioned container bar.
I don't want to iterate over my input or output range multiple times. The element of the problem I'm trying to solve are simply:
Append bar with a constructed vector of size min(STEP, distance(it, foo.end())
Have the it point to advance(it, size(bar.back())) after constructing the container
Do this vector construction in-place
The translation of my answer to the confines of this problem is:
auto it = cbegin(foo);
for (auto i = size(foo); i > STEP; i -= STEP) {
bar.push_back(decltype(bar)::value_type(STEP));
for (auto internalIt = bar.back().begin(); internalIt != bar.back().end(); ++internalIt, ++it) {
*internalIt = *it;
}
}
bar.push_back(decltype(bar)::value_type(it, cend(foo)));
The problem is this line: bar.push_back(decltype(bar)::value_type(STEP)) I'm allocating the vector and 0-initializing it's components. Is there a better way to do this, under which I still would only iterate over the input and output ranges once?
bar.push_back(decltype(bar)::value_type{}); // or bar.resize(bar.size() + 1); if you prefer
bar.back().reserve(STEP);
while (bar.back().size() < STEP) {
bar.back().push_back(*it);
++it;
}
It's a stroke of bad luck that std::copy_n returns the advanced output iterator, when what you need to keep is the advanced input iterator, otherwise you could use that in place of the loop (using std::back_inserter to get the destination iterator).
Feel free to use a counter variable if you're concerned about the performance of bar.back().size()!

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?

Iterator and 2d vector

vector< vector<int> >::iterator temp = mincost.end();
vector<int> a = *temp;
if ( *temp != *(temp--) )
return 0;
mincost is a 2d vector, I want to get the last vector<int> of this vector and last--.
I don't really understand about iterator :) . Help me !! :D
Thx ^^
minconst.end() points to the element one-past-the-end of the vector minconst; it doesn't point to the last element in the vector.
Since you want the last two elements, you should first test to be sure the vector actually has two elements in it, otherwise inevitably you'll run into problems. Then, accessing the last elements in the vector is simply a matter of *(minconst.end() - 1) and so forth.
The C++ Reference also has a description of iterators.
It would probably be helpful to learn about iterators in general.
A quick google search leads to many good references, not the least of which is
http://www.cppreference.com/wiki/stl/iterators
Good luck!
If you're new to STL containers, think of the end() iterator as something like the '\0' character in C-strings - they define where the end is, but the actual value they carry isn't useful. If you dereference the end iterator, you'll get junk, or most probably an exception.
Try this:
if (!mincost.empty())
{
//it contains atleast one 1-d vector and the 'end' iterator.
iter = mincost.end();
--iter;
//dereference iter here.
}
Once you're comfortable with thinking in terms of iterators, look up the reverse_iterator. As Effo mentioned, they are the best solution here.