C++ iterate container backwards N steps - c++

In C++ I can use reverse_iterator to loop elements of container, say, list, backwards.
How do I iterate over a certain number of elements? Not reaching the beginning? This code works, but I feel there is a better way.
std::list<int> mylist{1,2,3,4,5};
int cnt = 3;
for (auto rit = mylist.rbegin(); rit != mylist.rend(); ++rit) {
if (cnt == 0) break;
std::cout << *rit << " ";
--cnt;
}
Expected output {5, 4, 3}.

You can adjust the loop as follows:
for (auto rit = mylist.rbegin(); rit != std::next(mylist.rbegin(), 3); ++rit)
{
std::cout << *rit << " ";
}
but note that for this to work reliably, you need to check that the list is at least of size 3 or adjust the parameter to std::next as in const auto n = std::min<std::size_t>(3, mylist.size());.
With C++20, you should be able to go with (obviously not tested)
#include <ranges>
for (int n : mylist | std::ranges::reverse_view | std::ranges::take_view(3))
std::cout << n << "\n";
This renders the size testing superfluous, as take_view is bound by the range size (it performs this check internally).

Not reaching the beginning?
If you can guarantee that, then
std::copy_n(mylist.rbegin(), 3, std::ostream_iterator<int>(std::cout, " "));
Or to be safer
std::copy_n(mylist.rbegin(), std::min(mylist.size(), std::list<int>::size_type(3)), std::ostream_iterator<int>(std::cout, " "));
LIVE

I would not put incrementing/decrementing and checking the condition in the loop body, but rather put it where you most expect it:
std::list<int> mylist{1,2,3,4,5};
int cnt = 3;
for (auto rit = mylist.rbegin(); rit != mylist.rend() && cnt > 0; ++rit,--cnt) {
std::cout << *rit << " ";
}
Now you could for example add a continue anywhere in the loop body without making the loop go havoc.
PS: actually I like the other answers better, but I'll leave this one as it is minimal changes and imho incrementing / checking a loop counter inside the loop body is something to be avoided if possible (and it is possible here).

Related

Error during the usage of of size() function in vectors

So I've started learning vectors for the first time and wrote a simple program which goes like this:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> g1;
int n;
cout<<"enter values"<<endl;
do
{
cin>>n;
g1.push_back(n);
} while (n);
cout<<"Vector values are: "<<endl;
for(auto i=g1.begin(); i<g1.size();i++)
cout<<*i<<endl;
}
When I try executing it, an error shows up saying "type mismatch" at the g1.size() part. Why exactly does this happen? I used the auto keyword for the iterator involved and assumed there wouldn't be any problem?
That is the bad side of using auto. If you have no idea what the result of auto is, you get no idea why it is something totally different you expect!
std::vector::begin delivers a std::vector::iterator and you can't compare it against an size_type value which is a result of std::vector::size. This type is typically std::size_t
You have to compare against another iterator which is the representation of the end of the vector like:
for(auto i = g1.begin(); i != g1.end(); i++)
There are at least three ways to iterate through the contents of a vector.
You can use an index:
for (int i = 0; i < vec.size(); ++i)
std::cout << vec[i] << '\n';
You can use iterators:
for (auto it = vec.begin(); it != vec.end(); ++it)
std::cout << *it << '\n';
You can use a range-based for loop:
for (auto val : vec)
std::cout << Val <<'\n';
The latter two can be used with any container.
g1.begin() returns an iterator to the 1st element, whereas g1.size() returns the number of elements. You can't compare an iterator to a size, which is why you are getting the error. It has nothing to do with your use of auto, it has to do with you comparing 2 different things that are unrelated to each other.
You need to change your loop to compare your i iterator to the vector's end() iterator, eg:
for(auto i = g1.begin(); i != g1.end(); ++i)
cout << *i << endl;
Or, simply use a range-based for loop instead, which uses iterators internally:
for(auto i : g1)
cout << i << endl;
Otherwise, if you want to use size() then use indexes with the vector's operator[], instead of using iterators, eg:
for(size_t i = 0; i < g1.size(); ++i)
cout << g1[i] << endl;

Iterator becomes invalid after inserting in a boost::multi_index?

According to the documentation, hashed index iterators remains valid when new elements are inserted into a multi_index. However when I attempted the following approach
auto& myIndex = myMultiIndex.get<0>();
auto range = myIndex.equal_range(x);
for (auto iter = range.first; iter != range.second; ++iter) {
myMultiIndex.emplace(someArgsRelatedToIter);
}
the range.first/range.second seem to become invalid: even though std::distance(range.first, range.second) == 1, the for loop actually gets executed twice. Am I somehow not using it correctly? Thanks!
When c++ libraries specify that iterators aren't invalidated by an operation it means that the iterators still point to the same element. For example in the following code:
std::list< int > l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
auto first = l.begin();
auto last = std::find(l.begin(), l.end(), 3);
std::cout << *first << std::endl;
std::cout << *last << std::endl;
1, 3 and 2 are printed. If we now insert some elements:
l.insert(last, 4);
l.insert(last, 5);
std::cout << *first << "\n";
std::cout << *last << "\n";
std::cout << std::distance(first, last) << "\n";
first and last are still valid and point to the same elements but the distance is now 4 so they point to a different range.

Find all elements with the same value in a vector, then erase all of them out of the vector

The problem is that I need to find all elements with the same value in a vector, do something with them, then erase all of them out of the vector. Keep doing that until the vector is empty.
vector<unsigned> L;
vector<unsigned>::iterator it, it2, it3;
vector<unsigned> vec;
unsigned Z;
// populate the vector (1, 2, 3, 4, 2, 4)
for(unsigned i = 1; i <= 4; ++i)
L.push_back(i);
for(unsigned i = 2; i <= 4; i = i + 2)
L.push_back(i);
it = L.begin();
while(it != L.end() -1){
cout<< "*it = " << *it << endl;
Z=0;
vec.clear();
it2 = it + 1;
cout<< "*it2 = " << *it2 << endl;
while(it2 != L.end()){
cout << "Loop *it2 = " << *it2 <<endl;
if(*it == *it2){
vec.push_back(*it);
L.erase(it2); // iterator automatically points to the next element
cout<< "after erase(it2), *it2 = " << *it2 << endl;
continue;
}
++it2;
}
// do something (here I calculate the average)
for(it3 = vec.begin(); it3 != vec.end(); ++it3)
Z = Z+ *it3;
Z= Z/vec.size();
cout<< "Z = " << Z << endl << endl;
L.erase(it); // iterator automatically points to the next element
}
The output is:
*it = 1
*it2 = 2
Loop *it2 = 2
Loop *it2 = 3
Loop *it2 = 4
Loop *it2 = 2
Loop *it2 = 4
Then it stops working
If I populate the vector with this code
// populate the vector (1, 2, 3, 4, 1, 3)
for(unsigned i = 1; i <= 4; ++i)
L.push_back(i);
for(unsigned i = 1; i <= 4; i = i + 2)
L.push_back(i);
Then the output is
*it = 1
*it2 = 2
Loop *it2 = 2
Loop *it2 = 3
Loop *it2 = 4
Loop *it2 = 1
after erase(it2), *it2 = 3
Loop *it2 = 3
Z = 1
*it = 2
*it2 = 3
Loop *it2 = 3
Loop *it2 = 4
Loop *it2 = 3
It stops working here
I know there is something wrong in the second while loop but I can't figure out what it is. Any help would be appreciated.
If the goal is to
Process duplicates and then
Erase them
there is a much easier solution to this, and that is to use std::stable_partition, along with a std::set:
#include <algorithm>
#include <vector>
#include <iterator>
#include <set>
#include <iostream>
#include <numeric>
using namespace std;
int main()
{
std::vector<int> L = { 1, 2, 4, 3, 2, 4 };
std::set<int> tempSet;
//...
// partition the elements, unique items on left, duplicates on right
auto divider = stable_partition(L.begin(), L.end(), [&](int n)
{
// return true if item is not in the set, false otherwise
return tempSet.insert(n).second;
});
// do something with the duplicates, for example, print them
cout << "Here are the dups:\n";
copy(divider, L.end(), ostream_iterator<int>(cout, " "));
// get the average
// get number of duplicates
size_t numDups = std::distance(divider, L.end());
double avg = 0.0;
// compute average using std::accumulate
if ( numDups > 0 )
avg = std::accumulate(divider, L.end(), 0.0) / numDups;
cout << "\nThe average of the duplicates is: " << avg << "\n";
// erase the duplicates
L.erase(divider, L.end());
// print the updated vector now
cout << "\n\nHere is the resulting vector:\n";
copy(L.begin(), L.end(), ostream_iterator<int>(cout, " "));
}
Live Example, C++ 14
Here is the same code, but using C++ 03 (for those without C++11 / 14):
Live Example, C++ 03
Note there are no loops in the code above. Everything is done by an algorithm function. Partitioning, average computation, erasing, etc., are all performed with no loops.
Basically we test each item to see if the item exists in the set. If it does, then it will go to the right of the partition, if not, then the item goes to the left of the partition.
The return value divider is an iterator to the element that is the "dividing line" in the partition. Once the items are partitioned, then we can process them easily by using divider to tell us where the "good" items are and where the "about to be erased" items are.
BTW, this worked the first time I compiled it successfully -- the one major reason for this "luck" in getting it to work quickly is that the algorithm functions just plain work when given the correct parameters (and if the predicate function is written correctly). Erasing items, moving items, etc. in a container, especially a sequence container such as vector, is almost always covered by usage of 1, 2, or 3 algorithm functions.
This is my solution, which was inspired by PaulMcKenzie's code (please don't mind my re-using some of your lines). Just like him I didn't use any handwritten loops, but made use of STL's algorithms, which one should always consider first.
In my solution I only use another container of exactly the same type and only two simple algorithms: std::remove_if and std::find. This way the code makes the intention a little bit clearer: find duplicates and remove them. Also note the use of std::move that should come in handy when having a container of something more complex than int. In that case one may have to consider using std::find_if of course.
#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
int main()
{
std::vector<int> L = { 1, 2, 4, 3, 2, 4 };
using cont = decltype(L);
using vt = cont::value_type;
// find duplicates and move them to separate container
cont D;
D.reserve(L.size());
D.swap(L);
D.erase(std::remove_if(D.begin(), D.end(), [&L] (vt& value)
{
if (L.cend() == std::find(L.cbegin(), L.cend(), value))
{
L.emplace_back(std::move(value));
return true;
}
return false;
}), D.end());
// do something with the duplicates, for example, print them
std::cout << "Here are the dups:\n";
std::copy(D.cbegin(), D.cend(), std::ostream_iterator<vt>(std::cout, " "));
// print the vector now
std::cout << "\n\nHere is the resulting vector:\n";
std::copy(L.begin(), L.end(), std::ostream_iterator<vt>(std::cout, " "));
return 0;
}
See it in action here.
The problem is with your erase call. erase does not automatically update the iterator; it returns the iterator to the next element. To fix it, you need to use
it2 = L.erase(it2);
and later
it = L.erase(it);
vector<unsigned> L;
vector<unsigned>::iterator it, it2, it3;
vector<unsigned> vec;
unsigned Z;
// populate the vector (1, 2, 3, 4, 2, 4)
for(unsigned i = 1; i <= 4; ++i)
L.push_back(i);
for(unsigned i = 2; i <= 4; i = i + 2)
L.push_back(i);
std::set<unsigned int> myset;
for(it = L.begin(); it != L.end(); ++it){
myset.insert(*it);
}
for(std::set<unsigned int>::iterator iter = myset.begin();
iter != myset.end(); ++iter){
std::cout << "element = " << *iter << std::endl;
Z += *iter;
}
std::cout << "sum = " << Z << std::endl;
Z = Z/myset.size();
std::cout<< "Average value = " << Z << std::endl;
L.clear();
I finally found out the solution for my own question. The question is
We have a vector L that contains many duplicated elements.
We need to find all the dup elements
Do something with the dup elements
My method is to use 2 iterators on L, say it and it2. it is kept at the beginning of L, while it2 will go through the vector. If *it and *it2 have the same value, then we will put *it2 into a temporary vector vec. Then, we L.erase(it2). it2 will keep moving till the end of L, collect all the dup elements into vec and delete them from L. When it2 reach the end of L, we call L.erase(it). After that, the process continues until the vector L is empty.
Here is the code that I modified. The most important thing to note is the condition in the while. I use while(it < L.end()) instead of while(it != L.end()) because when we erase an element, the iterator will somehow go pass the condition !=.
I am using an old compiler (older than C++0x) for I don't want to mess with the environment right now. Thus, it will be safe for anyone to use my code.
vector<unsigned> L;
vector<unsigned>::iterator it, it2, it3, it4;
vector<unsigned> vec; //temporary variable to store matched elements
float Z;
// populate the vector (1, 2, 3, 4, 1, 3)
for(unsigned i = 1; i <= 4; ++i)
L.push_back(i);
for(unsigned i = 1; i <= 4; i = i + 2)
L.push_back(i);
it = L.begin();
while(it < L.end()){
cout<< "*it = " << *it << endl;
Z=0;
vec.clear();
vec.push_back(*it);
if(L.size() == 1){
cout << "Last element of vector = " << *it <<endl;
goto Loop1;
}
else
it2 = it + 1;
while(it2 < L.end()){
cout << "Loop *it2 = " << *it2 <<endl; //debug
if(*it == *it2){
vec.push_back(*it2);
L.erase(it2); // iterator automatically points to the next element
cout<< "after erase(it2), *it2 = " << *it2 << endl; //debug
continue;
}
++it2;
}
Loop1:
for(it3 = vec.begin(); it3 != vec.end(); ++it3)
Z = Z+ *it3;
Z= Z/vec.size();
cout<< "Z = " << Z << endl ;
if(L.empty()) break;
L.erase(it); // iterator automatically points to the next element
//debug
cout<< endl << "new vector = ";
for(it4 = L.begin(); it4 != L.end(); ++it4)
cout << *it4 << ' ';
cout << endl;
}
#PaulMcKenzie: I took me a while to catch on with your writing style in C++14 and the use of the STL functions which I am not familiar with. Your code is at very high level and it is hard for beginner. Anyway, I chose it as best answer for your contribution. I really learned a lot from you. Thank you for your quick responses.

how to iterate through a set of sets C++

Pretty new to C++, only at it a week or so, I want to iterate through a set of nested sets and write each element in the inner set to a line in a file.
Each inner set has 3 elements and I want all three elements on the same line.
I have a set up as follows:
// Define "bigSet" and initiate as empty set "Triplets"
typedef set < set<string> > bigSet;
bigSet Triplets;
I tried something of this sort to go through it but it gives me an error...
// Iterate through and print output
set <string>::iterator it;
for(it = Triplets.begin(); it != Triplets.end(); it++){
cout << *it << endl;
}
Any help is greatly appreciated guys thank you!
I would do it this way:
// Iterate through and print output
set < set <string> >::iterator it_ex; // iterator for the "outer" structure
set <string>::iterator it_in; // iterator for the "inner" structure
for(it_ex = Triplets.begin(); it_ex != Triplets.end(); it_ex++)
{
for(it_in = it_ex->begin(); it_in != it_ex->end(); it_in++)
cout << *it_in << ", ";
cout << endl;
}
Triplets is not a set<string>; it is a set<set<string>>; each item in Triplets is itself a set, than can contain several strings.
The iterator must match the type of the container; with two levels of nested containers, you should iterate twice:
set<set<string>>::iterator it;
set<string>::iterator it2;
for(it = Triplets.begin(); it != Triplets.end(); it++) {
for (it2 = it->begin(); it2 != it->end(); ++it2) {
cout << *it2 << endl;
}
}
Triplets is type set < set<string> > and therefore requires an iterator of type set < set<string> >::iterator or bigSet::iterator. It isn't type set <string>. You could also use const_iterator.
Note that iterating Triplets gives you an iterator to another set, and not a string.
Also consider
for (const auto& i : Triplets)
{
for (const auto& j : i)
{
cout << j << endl;
}
}
You have an error because Triplets.begin() is not of type set<string>::iterator, it's set<set<string>>::iterator.
What you need to do is have two loops: one for iterating over the outer set and one for the inner.
set<set<string>>::iterator it;
for(it = Triplets.begin(); it != Triplets.end(); ++it)
{
set<string>::iterator it2;
for(it2 = it->begin(); it2 != it->end(); ++it2)
{
cout << *it2;
}
cout << endl;
}
If you use increment/decrement operators (++/--) on iterators, it might be better to use the prefix versions (++it) instead of the suffix ones (it++). This is because the suffix ones create a copy of the iterator before it is incremented (and that copy is then returned) but in cases like this, you have no need for it.
Moreover, if you're using C++11, you can use the range-based for loops and auto keyword, which simplify things a lot:
for(const auto &innerSet : Triplets)
{
for(const auto &innerSetElement : innerSet)
{
cout << innerSetElement;
}
cout << endl;
}
First: if they're triplets, are you sure that std::set is the type you
want for the inner values. Perhaps a class would be more
appropriate, in which case, you define an operator<< for the `class,
and your simple loop works perfectly. Something like:
class Triplet
{
std::string x;
std::string y;
std::string z;
public:
// Constructors to enforce that none of the entries are identical...
// Accessors, etc.
friend std::ostream& operator<<( std::ostream& dest, Triplet )
{
dest << x << ", " << y << ", " << z;
return dest;
}
};
And then to output:
for ( Triplet const& elem : Triplets ) {
std::cout << elem << std::endl;
}
Otherwise: you need to define the format you want for the output. In
particular, you'll probably want a separator between the strings in the
line, for example. Which means you probably cannot use a range based
for, at least not for the inner loop. You would need something like:
for ( std::set<std::string> const& triplet : Triplets ) {
for ( auto it = triplet.cbegin(); it != triplet.cend(); ++it ) {
if ( it != triplet.cebegin() ) {
std::cout << ", ";
}
std::cout << *it;
}
std::cout << std::endl;
}
(If the set of triplets is large, you'll definitely want to consider
replacing std::endl with '\n'. But of course, if it is really
large, you probably won't be outputting to std::cout.)

C++ iterator behaviour in for vs while loops

I do not understand why iterating through a container with a for loop produces different results than iterating through it with a while loop. The following MWE illustrates this with a vector and a set of 5 integers.
#include <iostream>
#include <vector>
#include <set>
using namespace std;
int main()
{
vector<int> v;
set<int> s;
// add integers 0..5 to vector v and set s
for (int i = 0; i < 5; i++) {
v.push_back(i);
s.insert(i);
}
cout << "Iterating through vector with for loop.\n";
vector<int>::const_iterator itv;
for (itv = v.begin(); itv != v.end(); itv++) cout << *itv << ' ';
cout << '\n';
cout << "Iterating through set with for loop.\n";
set<int>::const_iterator its;
for (its = s.begin(); its != s.end(); its++) cout << *its << ' ';
cout << '\n';
cout << "Iterating through vector with while loop.\n";
itv = v.begin();
while (itv++ != v.end()) cout << *itv << ' ';
cout << '\n';
cout << "Iterating through set with while loop.\n";
its = s.begin();
while (its++ != s.end()) cout << *its << ' ';
cout << '\n';
}
The above produces:
Iterating through vector with for loop.
0 1 2 3 4
Iterating through set with for loop.
0 1 2 3 4
Iterating through vector with while loop.
1 2 3 4 0
Iterating through set with while loop.
1 2 3 4 5
The for loops work as expected but not the while loops. Since I'm using ++ as a postfix, I don't understand why the while loops behave as they do. Another mystery is why the while loop prints a 5 for set s, since this number was not inserted in s.
Your while loop is not equivalent to the for loop.
The for loop is equivalent to
itv = v.begin();
while(itv != v.end()) {
cout << *itv << ' ';
itv++;
}
Note that the increment happens after the cout. In your while loops, you do the increment in the test, before cout. Even though you use postincrement, the increment takes effect before your loop body is executed.
Write your while loops like I did there and the discrepancy should disappear.
When you iterate using the for loop you increment the iterator only after the body is evaluated. When you iterate using the while loop you increment the iterator after the check but before the body of the loop. Dereferencing the iterator in the last iteration of your while loops causes undefined behavior.
It could be because the compiler evaluates the its++ in the while expression first before evaluating the rest of the expression.
Since I'm using ++ as a postfix, I don't understand why the while loops behave as they do.
That's because first the while predicate is evaluated, and then (if the predicate was true) the body of the while loop. By the time you try to access the value in the body, the iterator was already incremented.
Just a few 'random' style hints, mainly showing algorithm use and modern C++11 features.
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
#include <iterator>
int main()
{
const std::vector<int> v { 0,1,2,3,4 };
const std::set<int> s { 0,1,2,3,4 };
for (auto element : v)
std::cout << element << ' ';
std::cout << '\n';
for (auto element : s)
std::cout << element << ' ';
std::cout << '\n';
std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
std::copy(s.begin(), s.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << '\n';
}