简体   繁体   English

在std :: unordered_map上进行迭代 <int, std::vector<Element> &gt;

[英]Iterator over std::unordered_map<int, std::vector<Element>>

I have a class storing some data in unordered_map. 我有一个在unordered_map中存储一些数据的类。 Let us assume that the class looks as follows: 让我们假设该类如下所示:

class Container {
    std::unordered_map<int, std::vector<Element>> map;
};

And for convenience, I would like to iterate over the contents of my Container using range-for-loop, so that I would get all the Elements in nested vectors. 为了方便起见,我想使用range-for-loop遍历Container的内容,以便将所有Elements嵌套在嵌套向量中。

for (const Element& e : Container(...)) {
    magic(e);
}

To do this I need to implement iterator, that would iterate over the elements of nested vectors. 为此,我需要实现迭代器,该迭代器将迭代嵌套向量的元素。 I tried to do it myself, but ended up with such a malfunctioning monstrosity, that I don't even dare to put it here. 我本人尝试这样做,但最终出现了如此令人毛骨悚然的怪兽,甚至都不敢放在这里。 I just provide a link to the code I wrote, but it's just terrible and it doesn't work properly because I was unable to implement end correctly. 我只是提供了指向我编写的代码的链接 ,但这很糟糕,而且由于我无法正确实现end而无法正常工作。

So my question is, can it be done in some elegant manner? 所以我的问题是,能否以某种优雅的方式完成? Or at least correctly? 或至少正确? Any help is much appreciated. 任何帮助深表感谢。

There is a trick I learned a long time ago to convert a normal function into a kind of generator (or coroutine, or whatever it is called these days). 我很久以前就学到了一个技巧,可以将普通函数转换为一种生成器(或协程,或者现在称为“协程”)。

Imagine that C++ had the yield keyword, like Python or C#. 想象一下,C ++具有yield关键字,例如Python或C#。 Then doing the iteration would be simple: 然后进行迭代将很简单:

void shift()
{
    for (auto mapIt = bag->begin(); mapIt != bat->end(); ++mapIt)
    {   
        for (auto vecIt = mapIt->second.begin(); vecIt != mapIt->second.end(); ++vecIt)
        {   
            yield *vecIt; //Alas, C++ cannot yield!
        }
    }
}

The transformation to a proper C++ function can be done semi-automatically, following these easy steps: 遵循以下简单步骤,可以半自动转换为适当的C ++函数:

1-Move all local variables to the top of the function: 1-将所有局部变量移到函数顶部:

void shift()
{
    mapIt_t mapIt; //assume the proper typedefs somewhere
    vecIt_t vecIt;

    for (mapIt = bag->begin(); mapIt != bat->end(); ++mapIt)
    {   
        for (vecIt = mapIt->second.begin(); vecIt != mapIt->second.end(); ++vecIt)
        {   
            yield *vecIt; //Alas, C++ cannot yield!
        }
    }
}

2-Transform the local variables into member variables. 2-将局部变量转换为成员变量。 Add a int m_state; 添加一个int m_state; member variable, initialized to 0 . 成员变量,初始化为0

mapIt_t m_mapIt;
vecIt_t m_vecIt;
int m_state = 0;
void shift()
{

    for (m_mapIt = bag->begin(); m_mapIt != bat->end(); ++m_mapIt)
    {   
        for (m_vecIt = m_mapIt->second.begin(); m_vecIt != m_mapIt->second.end(); ++m_vecIt)
        {   
            yield *m_vecIt; //Alas, C++ cannot yield!
        }
    }
}

3-Wrap the function in a switch (m_state) statement. 3将函数包装在switch (m_state)语句中。 Put a case 0: at the beginning: case 0:放在开头:

mapIt_t m_mapIt;
vecIt_t m_vecIt;
int m_state = 0;
void shift()
{switch (m_state) { case 0: //behold my fancy indentation!

    for (m_mapIt = bag->begin(); m_mapIt != bat->end(); ++m_mapIt)
    {   
        for (m_vecIt = m_mapIt->second.begin(); m_vecIt != m_mapIt->second.end(); ++m_vecIt)
        {   
            yield *m_vecIt; //Alas, C++ cannot yield!
        }
    }

}}

4-Replace each yield with the following statements: m_state = N; return; case N:; 4-用以下语句替换每个yieldm_state = N; return; case N:; m_state = N; return; case N:; , being N a different integer for each place used. ,对于每个使用的位置N一个不同的整数。 Add m_state = -1; return; case -1:; m_state = -1; return; case -1:; m_state = -1; return; case -1:; at the end of the function. 在函数的末尾。 Use a macro if you feel it is worth it. 如果您认为值得使用宏。

mapIt_t m_mapIt;
vecIt_t m_vecIt;
int m_state = 0;
void shift()
{switch (m_state) { case 0: //behold my fancy indentation!

    for (m_mapIt = bag->begin(); m_mapIt != bat->end(); ++m_mapIt)
    {   
        for (m_vecIt = m_mapIt->second.begin(); m_vecIt != m_mapIt->second.end(); ++m_vecIt)
        {   
            m_state = 1; return; case 1:;
        }
    }
    m_state = -1; return; case -1:;
}}

5-Done! 5完成! You can make the container as complex as you want: if you can write a function that does the full iteration, you will be able to convert it into an iterator class. 您可以根据需要使容器复杂:如果您可以编写执行完整迭代的函数,则可以将其转换为迭代器类。


With all that in mind I have written the following sample code, that seems to work fine. 考虑到所有这些,我编写了以下示例代码,这似乎很好用。

class Bag
{
public:
    typedef std::unordered_map<int, std::vector<std::string> > container_t;
    container_t cnt;
};

class BagIterator : public std::iterator<std::forward_iterator_tag, std::string>
{
public:
    friend BagIterator begin(const Bag &bag);
    friend BagIterator end(const Bag &bag);

    BagIterator()
    {
        m_bag = NULL;
        m_state = -1;
    }

    const value_type &operator*() const
    {
        return *m_vecIt;
    }
    BagIterator &operator++()
    {
        shift();
        return *this;
    }
    BagIterator operator++(int)
    {
        BagIterator tmp = *this;
        operator++();
        return tmp;
    }
    bool operator==(const BagIterator &r) const
    {
        if (m_state != r.m_state)
            return false;
        if (m_state == -1)
            return true;
        return m_bag == r.m_bag && m_mapIt == r.m_mapIt && m_vecIt == r.m_vecIt;
    }
    bool operator!=(const BagIterator &r) const
    {
        return !operator==(r);
    }
private:
    typedef Bag::container_t::const_iterator mapIt_t;
    typedef std::vector<std::string>::const_iterator vecIt_t;
    const Bag *m_bag;
    mapIt_t m_mapIt;
    vecIt_t m_vecIt;
    int m_state;

    void shift()
    {switch (m_state) { case 0:
        for (m_mapIt = m_bag->cnt.begin(); m_mapIt != m_bag->cnt.end(); ++m_mapIt)
        {
            for (m_vecIt = m_mapIt->second.begin(); m_vecIt != m_mapIt->second.end(); ++m_vecIt)
            {
                m_state = 1; return; case 1:;
            }
        }
        m_state = -1; return; case -1:;
    }}
};

BagIterator begin(const Bag &bag)
{
    BagIterator res;
    res.m_bag = &bag;
    res.m_state = 0;
    res.shift();
    return res;
}
BagIterator end(const Bag &bag)
{
    BagIterator res;
    res.m_bag = &bag;
    return res;
}

A few comments: 一些评论:

  • Note how I derived the custom iterator from std::iterator so that the standard library will know how it works. 请注意,我是如何从std::iterator派生自定义迭代std::iterator以便标准库知道其工作方式。
  • Note also how I implemented the operator==() . 还要注意我如何实现operator==() First it checks that the state is the same, then if both are end() ( m_state==-1 ), and finally if both point to the same element. 首先,它检查状态是否相同,然后检查两个状态是否均为end()m_state==-1 ),最后检查是否均指向同一元素。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用带预分配 std::unordered_map 的嵌套向量快速构造 unordered_map <int, vector<thing *> >?</int,> - Fast construction of unordered_map with nested vector with preallocation std::unordered_map<int, vector<Thing *>>? unordered_map 中的段错误<int, std::vector<double> &gt; - Seg fault in unordered_map <int, std::vector<double>> 如何将元素插入 std::unordered_map <int, vector<object*> ></int,> - How do I insert a element into a std::unordered_map<int, vector<Object*>> std::unordered_map 迭代器遵从问题 - std::unordered_map iterator deference problem std :: unordered_map的递增迭代器 - incrementing iterator of std::unordered_map 如何在std :: unordered_map中更新向量 <std::string, std::vector<int> &gt;没有复制? - How to update the vectors in a std::unordered_map<std::string, std::vector<int>> without copying? std :: vector或std :: list for std :: unordered_map存储桶? - std::vector or std::list for std::unordered_map buckets? 在这种情况下哪个更有效:std :: vector <bool> 或std :: unordered_map <int> ? - Which is more efficient in this scenario: std::vector<bool> or std::unordered_map<int>? 对std :: unordered_map进行Boost :: serialization <double, std::vector<double> &gt; - Boost::serialization of an std::unordered_map<double, std::vector<double>> 将std :: unordered_map值移动到std :: vector - Moving an std::unordered_map values to std::vector
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM