将多图表转换为一组集合

时间:2010-11-09 11:01:41

标签: c++ stl set multimap

我有一个multimap,我想获得一组集合 - 它们将多图中共享相同密钥的所有A类项目组合在一起。在STL中是否有内置的方法来执行此操作?

4 个答案:

答案 0 :(得分:2)

我认为没有内置方式。但是手动操作很容易:

std::multimap<key, value> mm;
// ...
std::multimap<key, value>::const_iterator i = mm.begin();
while (i != mm.end())
{
    std::multimap<key, value>::const_iterator end = mm.upper_bound(i->first);
    // construct a set from the values in [i, end)
    i = end;
}

或类似的东西。

答案 1 :(得分:1)

你可以在一对上使用一套。

首先定义一对。该对需要将键作为第一个元素,将实例作为第二个元素。

E.g。假设我们有一系列书籍,我们希望按作者分组:

typedef std::pair<Author *,Book *> AuthorBookPair;

然后在这一对上定义一个集合:

typedef set<AuthorBookPair> BooksGroupedByAuthor;

可以像这样填充集合:

BooksGroupedByAuthor books;
books.insert (std::make_pair(book1->getAuthor(),book1));
books.insert (std::make_pair(book2->getAuthor(),book2));
books.insert (std::make_pair(book3->getAuthor(),book3));
books.insert (std::make_pair(book4->getAuthor(),book4));

现在,您只需使用lower_bound和upper_bound方法查找作者的书籍:

#define POINTER_SMALLEST 0x00000000
#define POINTER_LARGEST  0xffffffff

BooksGroupedByAuthor::const_iterator lowerbound = books.lower_bound(std::make_pair(myFavoriteAuthor,POINTER_POINTER));
BooksGroupedByAuthor::const_iterator upperbound = books.upper_bound(std::make_pair(myFavoriteAuthor,POINTER_POINTER));

现在只需在lowerbound和upperbound之间进行迭代,即可获得该作者的所有书籍。

这个技巧依赖于我选择存储指向书籍的事实,并且我知道最小和最大的指针是什么(对于64位应用程序,您将不得不更改它!)。我必须承认这不是最好的伎俩。

稍微好一点的替代方法是自己存储书籍(如果你的应用程序允许复制这些实例)并制作2个特定的Book实例,分别代表“最小书”和“最大书”

关于这个技巧的好处是它允许在需要时添加更多尺寸。例如。您可以将年份添加为第二维,然后选择仅查找作者的书籍,或者查找特定年份的作者的书籍。使用更多维度时,新C ++ 0x中的元组可能会变得很方便。

这个技巧还有一个优点,就是它可以防止你两次添加一本书。如果一本书被添加两次,它仍然会在集合中一次(如果我们假设书的作者永远不会改变)。如果你要使用multi_map,你可以两次添加同一本书,这可能是不想要的。

答案 2 :(得分:1)

您可以按照以下方式(但使用更合适的名称)执行以下操作。请注意,输出结构实际上是集合的映射而不是一组集合,因为这样就可以保留密钥。

#include <map>
#include <set>


template <class key_t, class value_t>
struct transform_fn {
    typedef std::multimap<key_t, value_t> src_t;
    typedef std::map<key_t, std::set<value_t> > dest_t;

    dest_t operator()(src_t const& src) const
    {
        dest_t dest;
        typedef typename src_t::const_iterator iter_t;
        for (iter_t i = src.begin(), e = src.end(); i != e; ++i) {
            dest[i->first].insert(i->second);
        }
        return dest;
    }
};

#include <string>

int
main()
{
    typedef std::multimap<std::string, int> some_map_t;
    typedef std::map<std::string, std::set<int> > tr_some_map_t;

    some_map_t src;
    transform_fn<std::string, int> tr;
    tr_some_map_t dest = tr(src);

    return 0;
}

答案 3 :(得分:1)

这会创建一组集合。集合并没有多大意义。

您可以执行以下设置中的每个元素:

our_map[iter->first].insert(iter->second);

如果你有迭代器或

our_map[p.first].insert(p.second);

使用value_type对。

无论哪种方式,如果找不到iter-&gt;,则outer_set上的operator []将创建一个空的内部集合,如果该密钥已经存在,则将检索现有的内部集合。

这样可行,但不是最有效的方法。原因是我们知道p.first要么匹配我们看到的最后一个键,要么必须在最后插入,但上面每次都进行查找。因此,更有效的方法是保持我们的set迭代器。 value_type here是我们的multimap

的值类型
BOOST_FOREACH( elt, our_multimap )
{
    if( our_map.empty() || elt.key != last_key )
    {
       last_key = elt.key;
       map_iter = our_map.insert( 
          std::make_pair<elt.key, std::set<value_type>(), 
          our_map.end() ).first;
    }
    our_iter->insert( elt.value );
}

注意我们在插入时捕获迭代器,它是std :: map返回的第一个迭代器。

如果您不想使用迭代器,可以使用指向std :: set的指针。

std::set<value_type> *p_set = NULL;
key_type last_key;
BOOST_FOREACH( elt, our_multimap )
{
    if( !p_set || elt.key != last_key )
    {
       last_key = elt.key;
       p_set = &our_map[elt.key];
    }
    p_set->insert( elt.value );
}

这仍然具有在我们点击重复键时不必查找的优点,但是缺点是我们无法将“提示”传递给operator [],就像我们可以插入一样。