5

I'm currently studying the book Accelerated C++ (Koening/Moo) and I'm having trouble with one of the exercises. The task is to write a program which takes as an input some sequence of words which it then stores in a map<string, int>. The strings are the words entered and the associated int is the number of times each word occurs. You then have to sort the words by the number of times they occur; that is, by the value and not the key. You can't sort a map by the value, so I tried to copy the elements to a vector instead, which I intended to sort using a predicate. Unfortunately, all I get is a screen full of errors from g++. They seem to stem from the same place - putting the elements of my map into my vector, which I try to do like this:

int main()
{
    map<string, int> counters;

    cout << "Enter some words followed by end-of-file (ctrl-d): ";
    string word;
    while (cin >> word)
       ++counters[word];

    // Maps cannot be sorted by values, so pass the elements of counters to a vector.
    vector<map<string, int> > vec_counters;

    map<string, int>::const_iterator it = counters.begin();
    while (it != counters.end()) {
       vec_counters.push_back(*it);
       ++it;
    }
}

This is obviously just the first part, but I can't get even this to compile. I get the following error:

32:31: error: no matching function for call to std::vector, int> >::push_back(const std::pair, int>&)’ /usr/include/c++/4.5/bits/stl_vector.h:741:7: note: candidate is: void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::map, int>, _Alloc = std::allocator, int> >, value_type = std::map, int>]

What am I doing wrong?

honk
  • 9,137
  • 11
  • 75
  • 83
weatherwax007
  • 97
  • 1
  • 2
  • 6

5 Answers5

5

I'm pretty sure you aren't looking for a vector of maps:

#include <map>
#include <vector>
#include <string>
#include <iostream>

using namespace std;
int main()
{
    map<string, int> counters;

    cout << "Enter some words followed by end-of-file (ctrl-d): ";
    string word;
    while (cin >> word)
       ++counters[word];

    vector<std::pair<string, int> > vec(counters.begin(), counters.end());
}
sehe
  • 374,641
  • 47
  • 450
  • 633
  • @KerrekSB: not. I just copied the sample from the OP, and I can't break my good habits :) (_hi, by the way_) – sehe Jan 24 '12 at 23:52
  • Thanks sehe, stupid mistake on my part - obviously there is only one map. I didn't know that each element of a map was referred to as a pair, though. KarrekSB: you mention partial use of std:: in the actual code. I tend to put in all the 'using std::cin;' type lines at the start. Is that considered bad practice? – weatherwax007 Jan 25 '12 at 10:49
2

A bit off-topic, but here's a sexy solution using a bimap, which is a map in which both sides work as keys.

#include <iostream>
#include <sstream>
#include <string>
#include <boost/bimap.hpp>
#include <boost/bimap/list_of.hpp>
#include <boost/bimap/set_of.hpp>

int main()
{
    boost::bimap<boost::bimaps::set_of<std::string>, boost::bimaps::list_of<int>> m;

    for (std::string line; std::getline(std::cin, line); )
    {
        ++m.left[line];
    }

    auto & bml = m.left;
    auto & bmr = m.right;

    bmr.sort();

    for (auto const & p : bml) { std::cout << p.first << " => " << p.second << "\n"; }
    for (auto const & p : bmr) { std::cout << p.first << " => " << p.second << "\n"; }
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
1

You want to put the elements of a map in a vector. Maps are made out of pairs, not other maps. Therefore, your vector should be a vector of pairs.

That said, it looks like the reason you want the vector in the first place is to sort by values in the map. Why not then make your map map< int, string > instead? This will result in it being sorted in ascending order by the ints : Elements in the map are sorted from lower to higher key value following a specific strict weak ordering criterion.

Carl
  • 43,122
  • 10
  • 80
  • 104
0

If you want to sort your vector after inserting your map entries into it, then you can also use a std::set instead. The elements in a set are always sorted. This is especially helpful if you want to insert new elements at a later time. However, as you want to sort your map entries by value, you have to store flipped key-value pairs into the set. I omitted reading the input in the following C++11 code for the sake of clarity:

int main() {
    std::map<std::string, int> counters{
        {"sorted", 2}, {"value" , 5}, {"is" , 2}, {"by" , 3}, {"this" , 1}
    };

    std::set<std::pair<int, std::string>> s;  // Use a set instead of a vector.

    for (auto const &kv : counters)
        s.emplace(kv.second, kv.first);  // Flip the pairs.

    for (auto const &vk : s)
        std::cout << vk.second << ": " << vk.first << std::endl;

    return 0;
}

Output:

this: 1
is: 2
sorted: 2
by: 3
value: 5

If you want to sort in descending order, you can define the set as follows:

using flippedPair = std::pair<int, std::string>;
std::set<flippedPair, std::greater<flippedPair>> s;

Shorter C++17 code on Coliru

honk
  • 9,137
  • 11
  • 75
  • 83
0

Dereferencing a std::map<std::string, int>::const_iterator gives you a std::pair<std:string, int>, not a std::map<std::string, int>, so instead of this:

vector<map<string, int> > vec_counters;

you want this:

vector<std::pair<string, int> > vec_counters;
ruakh
  • 175,680
  • 26
  • 273
  • 307
  • This question is probably one of the most obscure C++ STL usage questions ever. Following up with OP's question, does `std::pair` still give you the benefit of `std::map`'s fast retrieval? – Xavier Ho Jan 24 '12 at 23:43
  • @XavierHo: A `std::pair` has a single `std::string` and a single `int`, so it's actually much faster retrieval than `std::map`, which has all the overhead of potentially having multiple elements. – ruakh Jan 24 '12 at 23:55
  • After re-reading OP's question, I think I misunderstood his intention. Yes, `std::pair` will be the best choice for the use case. I was thinking actually having a `std::map` in a `std::vector`, which is a different data structure entirely. – Xavier Ho Jan 25 '12 at 00:01