Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make an iterator over fields of container's items?

So, say, I have a class that has std::list<std::pair<int, std::string>> inside of it; how can I implement an iterator for this class such that it iterates over strings contained in this list?

Or, for example, in my class I have a vector of structs with fields a, b and c; can I create an iterator (maybe inherit vector's iterator? don't know), which, when dereferenced, will return a std::pair, corresponding to (b, c)?

By iterator I mean something like std::vector's iterators: something, that I can get via whatever.begin() and iterate over, as mentioned, strings inside the list.

UPD Okay, here's more info on what I want. In my HashMap class I have items: a list of structs: each with a key, a value and a pointer to it's place in the table. What I need is an iterator; but not the one that I can get by doing items.begin(), since this iterator, when dereferenced, will return my struct. I need an iterator, such that I can return it when user calls HashMap.begin(), and it should dereference into a std::pair, corresponding to (key, value).

This should, hopefully, make my question clearer.

UPD2 Here is my struct, if that helps:

template<class KeyType, class ValueType>
struct node {
    KeyType key;
    ValueType value;
    node** place;

    node(KeyType key_ = KeyType(), ValueType value_ = ValueType()): key(key_), value(value_) {};
};
like image 368
Akiiino Avatar asked Sep 06 '25 03:09

Akiiino


1 Answers

One elegant way is to use transforming iterators or a transformed range:

#include <iostream>
#include <list>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/iterator/transform_iterator.hpp>

int main() {
    std::list<std::pair<int, std::string>> l;
    auto extractor = [](auto&& elem) { return elem.second; };

    // Using a transformed range.
    for(auto&& v: l | boost::adaptors::transformed(extractor))
        std::cout << v << '\n';

    // Using transform iterators.
    for(auto i = boost::make_transform_iterator(l.begin(), extractor), j = boost::make_transform_iterator(l.end(), extractor); i != j; ++i)
        std::cout << *i << '\n';
}

To use a transform iterator for you containers you can do something like:

struct MyContianer
{
    std::list<std::pair<int, std::string>> container;

    static auto constexpr first_extractor = [](auto&& elem) { return elem.second; };
    using iterator_first = decltype(boost::make_transform_iterator(container.begin(), first_extractor));
    iterator_first begin_first() { return {container.begin(), first_extractor}; }
    iterator_first end_first() { return {container.end(), first_extractor}; }

    static auto constexpr second_extractor = [](auto&& elem) { return elem.second; };
    using iterator_second = decltype(boost::make_transform_iterator(container.begin(), second_extractor));
    iterator_second begin_second() { return {container.begin(), second_extractor}; }
    iterator_second end_second() { return {container.end(), second_extractor}; }

};
decltype(MyContianer::first_extractor) constexpr MyContianer::first_extractor;
decltype(MyContianer::second_extractor) constexpr MyContianer::second_extractor;

int main() {
    MyContianer c;
    c.begin_first();
    c.begin_second();
}
like image 93
Maxim Egorushkin Avatar answered Sep 08 '25 01:09

Maxim Egorushkin