19

I am looking for a readable, elegant way to do the following in C++, here shown in Python:

for datum in data[1:]:
    # do work.

The iterators on the data in question may not support random access iterators, so I can't just use:

for (mIter = data.begin() + 1; mIter != data.end(); mIter++)

The best I've come up with is the following:

iterable::iterator mIter = data.begin();
for (mIter++;  mIter != allMjds.end(); mjdIter++) {
    // do work.
}

It's not too lengthy, but it's hardly expository - at first glance it actually looks like a mistake!

Another solution is to have an "nth element" helper function, I guess. Is there a more concise way?

user511493
  • 263
  • 1
  • 2
  • 7

7 Answers7

32

You can use std::next(iter, n) for a linear-time advance. You can also use the standard std::advance algorithm, though it isn't as simple to use (it takes the iterator by a non-const reference and doesn't return it).

For example,

for (mIter = std::next(data.begin()); mIter != data.end(); ++mIter)

or,

mIter = data.begin();
std::advance(mIter, 1);
for (; mIter != data.end(); ++mIter)

Note that you must make sure that data.size() >= 1, otherwise the code will fail in a catastrophic manner.

avakar
  • 32,009
  • 9
  • 68
  • 103
  • 1
    What is the library for `boost::next`? Thanks – Steve Townsend Nov 18 '10 at 00:12
  • 1
    @Steve Townsend: Utility. http://www.boost.org/doc/libs/1_44_0/libs/utility/utility.htm#functions_next_prior – Fred Larson Nov 18 '10 at 00:12
  • 4
    It's probably worth noting, too, that `next` and `prior` are constant time if you DO have random-access iterators. – Fred Larson Nov 18 '10 at 00:17
  • +1, with the remark I added to @Steve Townsend's answer and @wilhelmtell's comment there. – Fred Foo Nov 18 '10 at 00:23
  • @avakar: It would be great if a checked version of `next` existed, to which you can pass the `end` iterator, and that will advance n times unless it meets `end`. For the random access version, one can do the check prior to the call, but for all the other linear versions, doing the check means essentially advancing the iterator, and there is no longer a reason to call `next` at this point :/ – Matthieu M. Nov 18 '10 at 08:13
8
#include <iterator>

iterator iter = data.begin();
for (advance(iter, 1); iter != data.end(); ++iter)
{
  // do work
}

This relies on >= 1 element in data to avoid an exception, though.

Steve Townsend
  • 53,498
  • 9
  • 91
  • 140
  • 1
    +1, with the remark that "this relies on > 1 element in `data`" means no exception when the precondition is not met (in general, that is). – Fred Foo Nov 18 '10 at 00:12
  • 3
    Using the `advance` call as the init statement, nice touch! :) +1 – avakar Nov 18 '10 at 00:13
2

You could try:

for (mIter = data.begin() ; ++mIter != data.end() ; )

but you'd need to make sure that if data.begin () == data.end () doing the ++mIter doesn't cause a problem.

Since this is a non-standard for loop, using a while loop might be more appropriate as there are fewer preconceived ideas about how they work, i.e. people looking at your code are more likely to read a while statement than a for statement as there is usually a model of how a for loop should work in their head.

mIter = data.begin ();

while (++mIter != data.end ())
{
}
Skizz
  • 69,698
  • 10
  • 71
  • 108
1

You can use boost::next for this (but you should be sure that the list actually has an element in it before doing so):

#include <algorithm>
#include <iostream>
#include <iterator>
#include <list>

#include <boost/assign.hpp>
#include <boost/next_prior.hpp>
using namespace boost::assign;

int main()
{
    std::list<int> lst = list_of(23)(9)(84)(24)(12)(18);
    std::copy(boost::next(lst.begin()), lst.end(), std::ostream_iterator<int>(std::cout, " "));
    return 0;
}
Stuart Golodetz
  • 20,238
  • 4
  • 51
  • 80
1
iterable::iterator mIter = data.begin();    
std::for_each(++mIter, data.end(), some_func);

where some_func contains the code you want to execute... you could even trivialise it with a simple wrapper function

template <typename _cont, typename _func>
for_1_to_end(_cont const& container, some_func func)
{
  typename _cont::const_iterator it = _cont.begin();
  std::for_each(++it, _cont.end(), func);
}
Nim
  • 33,299
  • 2
  • 62
  • 101
0

This is how i would do it

// starting position in the list
int i = 4;

// initialize "it" to point to the first item of data.
std::list<int>::iterator it = data_list.begin();

if (i < data.size()) {
    // loop starting from 4 to end of the list.
    for (std::advance(it, i); it != token_list.end(); it++) {
        //use "it" here
    }
}
else {
    // Error: starting point is greater than size of data
}
winux
  • 452
  • 4
  • 12
0

What might be a good solution in a modern c++ way :

std::for_each(cbegin(data)+1,cend(data),[&](const auto& elem)
{
    //do whatever you want with elem here
});

This will work even if data is empty. It's basically possible to use this in the exact same way as you would do it with a standard for-range loop and has the advantage not to require any additional variable while keeping the code readable.

Algorithm library : https://en.cppreference.com/w/cpp/algorithm

Cypher
  • 11
  • 2