135

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.

kevin
  • 1,834
  • 3
  • 18
  • 23

5 Answers5

135

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.
gsamaras
  • 71,951
  • 46
  • 188
  • 305
codaddict
  • 445,704
  • 82
  • 492
  • 529
  • 6
    This misses the fact that `std::vector` has random access iterators. – sbi Mar 07 '10 at 06:38
  • 29
    Regardless of whether you know the iterator type is random-access or not, the "best" way to move an iterator forward n spaces is not to write your own loop, but to call `std::advance(it, n)`. It's defined to do exactly what you want, and it will automatically use `it + n` if the iterator is tagged as random-access, or do the loop if it has to. – Steve Jessop Mar 07 '10 at 13:47
96

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

lashgar
  • 5,184
  • 3
  • 37
  • 45
  • 8
    WHY DOES THIS HAVE ZERO LIKES?! <3 – jperl Jul 12 '18 at 00:38
  • 5
    @jperl Posted 8 years late. Will take the next 8 years to get enough upvotes :) – lashgar Jul 12 '18 at 17:15
  • 1
    @jperl, well the answer is off-topic. Although this loop feature is nice, it doesn't help you to know when you're at the nth element, which is the OP's question. Also, any answer that requires O(n) time complexity, such as this one, is very bad. Accessing the nth element of a vector should always be O(1). – Elliott Feb 01 '20 at 10:17
  • @lashgar I tried this with array but failed. Does it work for array? – era s'q Jun 08 '20 at 10:38
  • @eras'q, tried with gcc `7.5.0` on Ubuntu 18.04 and works for array the same way. – lashgar Jun 08 '20 at 17:55
68

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.

holzkohlengrill
  • 1,044
  • 19
  • 29
Michael Aaron Safyan
  • 93,612
  • 16
  • 138
  • 200
23

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;
Jim Fell
  • 13,750
  • 36
  • 127
  • 202
UncleBens
  • 40,819
  • 6
  • 57
  • 90
  • 1
    You can use `advance` for random-access iterators too, or iterators of unknown category, since it is guaranteed to operate in constant time in that case. This is why user-defined iterators should be correctly tagged. – Steve Jessop Mar 07 '10 at 13:50
  • Indeed, but `advance` is really annoying to use (because of the out parameter usage) if you know you are dealing with random access iterators. Would only recommend in generic code, and if not used a lot (if the algorithm doesn't support non-random-access iterators well, so be it - for example, `std::sort` *could* sort a `std::list` but it doesn't because it would be ridiculously inefficient). – UncleBens Mar 07 '10 at 14:03
  • Sure, the classic example would be if your algorithm only actually *needs* an InputIterator, but for whatever reason it sometimes skips ahead, so you want it to be more efficient if the iterator does have random access. It's not worth restricting your algorithm to random access only by using `operator+`. But the question was explicitly about vector, so there's nothing wrong with the first part of your answer. I just thought the second part might imply "you can't use advance with random access iterators, even if you want to" to someone who has never seen `advance` before. – Steve Jessop Mar 07 '10 at 14:10
  • OK, reworded that bit and gave the example with a vector. – UncleBens Mar 07 '10 at 14:42
  • second line, `Vector` should be lower case – Lei Yang Jul 31 '17 at 02:49
  • The first sentence is crucial, IMHO. I used to find STL iterators awkward, cryptic and unnatural to use until I realized they are like pointers - that was "eureka" moment for me. – el.pescado - нет войне Sep 26 '17 at 06:49
  • wow, I didn't know `it[n]` is also possible! Where can I find the reference for that? I only found the operator[] for std::vector itself, not for its iterator, in cppreference.com. – starriet Jun 27 '23 at 21:16
1

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());
hmofrad
  • 1,784
  • 2
  • 22
  • 28