Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find the index of current object in range-based for loop?

Tags:

c++

iterator

Assume I have the following code:

vector<int> list; for(auto& elem:list) {     int i = elem; } 

Can I find the position of elem in the vector without maintaining a separate iterator?

like image 288
Fred Finkle Avatar asked Jun 09 '12 15:06

Fred Finkle


People also ask

What is loop index in for loop?

An index loop repeats for a number of times that is determined by a numeric value. An index loop is also known as a FOR loop.

How do you find the index for each?

Get The Current Array Index in JavaScript forEach()forEach(function callback(v) { console. log(v); }); The first parameter to the callback is the array value. The 2nd parameter is the array index.


1 Answers

Yes you can, it just take some massaging ;)

The trick is to use composition: instead of iterating over the container directly, you "zip" it with an index along the way.

Specialized zipper code:

template <typename T> struct iterator_extractor { typedef typename T::iterator type; };  template <typename T> struct iterator_extractor<T const> { typedef typename T::const_iterator type; };   template <typename T> class Indexer { public:     class iterator {         typedef typename iterator_extractor<T>::type inner_iterator;          typedef typename std::iterator_traits<inner_iterator>::reference inner_reference;     public:         typedef std::pair<size_t, inner_reference> reference;          iterator(inner_iterator it): _pos(0), _it(it) {}          reference operator*() const { return reference(_pos, *_it); }          iterator& operator++() { ++_pos; ++_it; return *this; }         iterator operator++(int) { iterator tmp(*this); ++*this; return tmp; }          bool operator==(iterator const& it) const { return _it == it._it; }         bool operator!=(iterator const& it) const { return !(*this == it); }      private:         size_t _pos;         inner_iterator _it;     };      Indexer(T& t): _container(t) {}      iterator begin() const { return iterator(_container.begin()); }     iterator end() const { return iterator(_container.end()); }  private:     T& _container; }; // class Indexer  template <typename T> Indexer<T> index(T& t) { return Indexer<T>(t); } 

And using it:

#include <iostream> #include <iterator> #include <limits> #include <vector>  // Zipper code here  int main() {     std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};      for (auto p: index(v)) {         std::cout << p.first << ": " << p.second << "\n";     } } 

You can see it at ideone, though it lacks the for-range loop support so it's less pretty.

EDIT:

Just remembered that I should check Boost.Range more often. Unfortunately no zip range, but I did found a pearl: boost::adaptors::indexed. However it requires access to the iterator to pull of the index. Shame :x

Otherwise with the counting_range and a generic zip I am sure it could be possible to do something interesting...

In the ideal world I would imagine:

int main() {     std::vector<int> v{1, 2, 3, 4, 5, 6, 7, 8, 9};      for (auto tuple: zip(iota(0), v)) {         std::cout << tuple.at<0>() << ": " << tuple.at<1>() << "\n";     } } 

With zip automatically creating a view as a range of tuples of references and iota(0) simply creating a "false" range that starts from 0 and just counts toward infinity (or well, the maximum of its type...).

like image 97
Matthieu M. Avatar answered Sep 22 '22 08:09

Matthieu M.