Подтвердить что ты не робот

Как я могу получить std:: набор ключей для std:: map

Сегодня утром я писал алгоритм, и я столкнулся с любопытной ситуацией. У меня два std::map s. Я хочу выполнить набор пересечений на наборах ключей каждого (чтобы найти, какие ключи являются общими для обеих карт). В какой-то момент в будущем, я думаю, что, вероятно, мне также понадобится выполнить вычитание здесь. К счастью, STL включает функции для обеих этих операций. Проблема в том, что я не могу получить std::set ключей из std::map. Есть какой-либо способ сделать это? Я ищу что-то, что было бы так просто, как в Java:

std::set<Foo> keys = myMap.getKeySet();

Я понимаю, что я не могу использовать функцию std::set_intersection() непосредственно на итераторах в карты, потому что в картах отображаются объекты std::pair вместо простых ключей. Кроме того, я не думаю, что карта гарантирует заказ. Я также заинтересован в выполнении этой же операции на паре std::multimap s, если это имеет значение.

EDIT. Я забыл упомянуть изначально, что из-за возраста компилятора, который я вынужден использовать (MSVС++ 6), большинство изящных шаблонных трюков, доступных в boost, не могут быть б.

4b9b3361

Ответ 2

В основном вы хотите получить копию, так как std:: map не хранит ключи в std:: set. std:: copy предполагает, что типы значений совместимы, что здесь не так. Std:: map:: value_type - std:: пара. Вы хотите скопировать только первую часть пары, что означает, что вам нужен std:: transform. Теперь, поскольку вы будете использовать insert_iterator на наборе, порядок не имеет значения. Std:: set будет сортировать по вставке, даже если карта уже была отсортирована.

[edit] Код может быть проще. Вершина моей головы, не скомпилирована.

std::transform(MyMap.begin(), MyMap.end(),
    std::inserter(MySet, MySet.end()),
    boost::bind(&std::pair<Key,Value>::first, _1));

Если у вас есть SGI select1st, вам не нужен boost:: bind.

[править] Обновлено для С++ 14

std::transform(MyMap.begin(), MyMap.end(),
    std::inserter(MySet, MySet.end()),
    [](auto pair){ return pair.first; });

Ответ 3

Карта действительно гарантирует заказ; поэтому он назвал отсортированный ассоциативный контейнер. Вы можете использовать set_intersection с пользовательской функцией компаратора, второй вариант указан здесь.

Итак, что-то вроде

bool your_less(const your_map::value_type &v1, const your_map::value_type &v2)
{ return v1.first < v2.first; }

set_intersection(m1.begin(), m1.end(), m2.begin(), m2.end(), your_output_it, your_less);

должен сделать трюк. (Также можно использовать boost:: lambda и bind, чтобы избежать записи временной функции.)

Оператор по умолчанию < более пары сравнивают оба компонента. Так как вам нужна эквивалентность только над первой частью пары (ключ карты), вам нужно определить свой собственный оператор сравнения, который обеспечивает такое отношение (что и делает функция выше).

Ответ 4

На практике

yourmap::const_iterator mi;
set<key_type> k;
for (mi = yourmap.begin(); mi != yourmap.end(); ++mi)
  k.insert(mi->first);
return k; 

Ответ 5

Вы можете просто перебрать и добавить каждую клавишу в набор. Наборы и карты являются как упорядоченными, так и неупорядоченными вариантами.

Ответ 6

Я нашел хорошую ссылку для вашего вопроса здесь

и у вас есть код для вашей проблемы:

    #include <iostream>
    #include <map>
    #include <set>
    #include <iterator>

    typedef std::map<std::string, int> MyMap;

    // also known as select1st in SGI STL implementation
    template<typename T_PAIR>
    struct GetKey: public std::unary_function<T_PAIR, typename T_PAIR::first_type>
    {
        const typename T_PAIR::first_type& operator()(const T_PAIR& item) const
        {
            return item.first;
        }
    };

    int main(int argc, char** argv)
    {
        MyMap m1,m2;

        m1["a"] = 1;
        m1["b"] = 2;
        m2["c"] = 3;
        m2["b"] = 3;

        std::set<std::string> s;
        std::transform(m1.begin(), m1.end(), std::inserter(s, s.begin()), GetKey<MyMap::value_type>());
        std::transform(m2.begin(), m2.end(), std::inserter(s, s.begin()), GetKey<MyMap::value_type>());
        std::copy(s.begin(), s.end(), std::ostream_iterator<std::string>(std::cout, " "));
        std::cout << std::endl;
        return 0;
    }

Ответ 7

Лучшим решением, не поддерживающим SGI, без ускорения STL, является расширение map:: iterator следующим образом:

template<typename map_type>
class key_iterator : public map_type::iterator
{
public:
    typedef typename map_type::iterator map_iterator;
    typedef typename map_iterator::value_type::first_type key_type;

    key_iterator(const map_iterator& other) : map_type::iterator(other) {} ;

    key_type& operator *()
    {
        return map_type::iterator::operator*().first;
    }
};

// helpers to create iterators easier:
template<typename map_type>
key_iterator<map_type> key_begin(map_type& m)
{
    return key_iterator<map_type>(m.begin());
}
template<typename map_type>
key_iterator<map_type> key_end(map_type& m)
{
    return key_iterator<map_type>(m.end());
}

а затем используйте их так:

        map<string,int> test;
        test["one"] = 1;
        test["two"] = 2;

        set<string> keys;

//      // method one
//      key_iterator<map<string,int> > kb(test.begin());
//      key_iterator<map<string,int> > ke(test.end());
//      keys.insert(kb, ke);

//      // method two
//      keys.insert(
//           key_iterator<map<string,int> >(test.begin()),
//           key_iterator<map<string,int> >(test.end()));

        // method three (with helpers)
        keys.insert(key_begin(test), key_end(test));

Ответ 8

Возможно, вы можете создать итератор для карты, которая дает только ключи с помощью boost:: adapters:: map_key, см. пример в boost:: adapters:: map_key documentation. Это, похоже, было введено в Boost 1.43 и должно быть совместимо с С++ 2003, но я не знаю о VС++ 6 конкретно, который относится к эпохе С++ 98.

Ответ 9

Построение ответа от zvrba и комментарий от дианота:

Просто сделайте приемную коллекцию вектором пар вместо карты, а проблема, указанная в дианатоме, закончилась. Итак, используя пример zvrba:

std::vector<std::pair<keytype, valtype>> v;

set_intersection(m1.begin(), m1.end(), m2.begin(), m2.end(),
std::back_inserter(v), []( std::pair<keytype, valtype> const & a,
std::pair<keytype, valtype> const & b){return a.first < b.first;});

Таким образом, без создания промежуточных копий или множеств мы можем эффективно получить пересечение двух карт. Эта конструкция компилируется с помощью gcc5.3.