Вопрос

У меня есть мультикарта, и я хотел бы получить набор наборов, которые группировали бы все элементы типа A в мультикарте, имеющие один и тот же ключ.Есть ли встроенный способ сделать это в STL?

Это было полезно?

Решение

Я не думаю, что есть встроенный способ. Однако это легко сделать вручную:

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;
}

Или что-то типа того.

Другие советы

Вы можете использовать набор на пару.

Сначала вы определяете пару.Паре нужен ключ в качестве первого элемента и ваш экземпляр в качестве второго элемента.

Например.Предположим, у нас есть коллекция книг, и мы хотим сгруппировать их по авторам:

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));

Теперь вы можете просто искать книги автора, используя методы low_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));

Теперь просто перебирайте нижнюю и верхнюю границы, чтобы получить все книги этого автора.

Этот трюк основан на том факте, что я решил хранить указатели на книги и знаю, что такое самый маленький и самый большой указатель (для 64-битных приложений вам придется это изменить!).Надо признать, это не самый красивый трюк.

Немного лучшей альтернативой было бы хранить сами книги (если в вашем приложении разрешено делать копии этих экземпляров) и создавать два конкретных экземпляра Book, которые представляют собой «самую маленькую книгу» и «самую большую книгу» соответственно.

Плюс этого трюка в том, что он позволяет при необходимости добавить больше измерений.Например.вы можете добавить год в качестве второго измерения, а затем выбрать поиск книг только автора или поиск книг автора определенного года.При использовании большего количества измерений могут пригодиться кортежи из нового C++0x.

Преимущество этого трюка еще и в том, что он защищает вас от повторного добавления книги.Если книга добавлена ​​дважды, она все равно окажется в коллекции один раз (если предположить, что автор книги никогда не меняется).Если бы вы использовали multi_map, вы могли бы добавить одну и ту же книгу дважды, что, вероятно, нежелательно.

Вы можете сделать что -то вроде (но с более подходящими именами) следующим образом. Обратите внимание, что выходная структура на самом деле является картой наборов, а не набором набора, потому что таким образом вы сохраняете ключи.

#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;
}

Это создает карту наборов. Набор наборов на самом деле не имеет смысла.

Для каждого элемента в вашем наборе вы можете сделать:

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

Если у вас есть итераторы или

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

с парами value_type.

В любом случае, Operator [] on outter_set создаст пустой внутренний набор, если ITER-> сначала не найден, и получит существующий, если ключ уже существует.

Это будет работать, но не будет наиболее эффективным способом сделать это. Причина в том, что мы знаем, что P.First либо соответствует последнему ключу, который мы видели, либо мы должны вставить в конце, но приведенное выше каждый раз делает поиск. Таким образом, более эффективным способом является удержание нашего итератора. value_type Вот тип значения нашего мультимапа

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 :: Так.

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 );
}

Это все еще имеет преимущество в том, что не нужно смотреть вверх, когда мы достигли дублированного ключа, но имеет недостаток, что мы не можем передать «намек» оператору [], как мы могли вставить.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top