0

I have this small program that reads a line of input & prints the words in it, with their respective number of occurrences. I want to sort the elements in the map that stores these values according to their occurrences. I mean, the words that only appear once, will be ordered to be at the beginning, then the words that appeared twice 7 so on. I know that the predicate should return a bool value, but I don't know what the parameters should be. Should it be two iterators to the map? If some one could explain this, it would be greatly appreciated. Thank you in advance.

#include<iostream>
#include<map>

using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::map;

int main()
{
    string s;
    map<string,int> counters;   //store each word & an associated counter

    //read the input, keeping track of each word & how often we see it
    while(cin>>s)
    {
        ++counters[s];
    }

    //write the words & associated counts
    for(map<string,int>::const_iterator iter = counters.begin();iter != counters.end();iter++)
    {
        cout<<iter->first<<"\t"<<iter->second<<endl;
    }

    return 0;
}
Ramila
  • 135
  • 2
  • 7
  • 18

6 Answers6

8

std::map is always sorted according to its key. You cannot sort the elements by their value.

You need to copy the contents to another data structure (for example std::vector<std::pair<string, int> >) which can be sorted.

Here is a predicate that can be used to sort such a vector. Note that sorting algorithms in C++ standard library need a "less than" predicate which basically says "is a smaller than b".

bool cmp(std::pair<string, int> const &a, std::pair<string, int> const &b) {
  return a.second < b.second;
}
hrnt
  • 9,882
  • 2
  • 31
  • 38
  • 2
    I will add that there is a concise syntax to do that: `std::vector > v(counters.begin(), counters.end());` – Jeremiah Willcock Feb 07 '11 at 15:37
  • so does that mean that maps take no predicates? Forgive me if this is a silly question, I'm still a student. – Ramila Feb 07 '11 at 16:18
  • You can give std::map a predicate as a type parameter, but that predicate is used to sort keys, not values. std::map requires the sort predicate so it can provide fast lookup. – hrnt Feb 07 '11 at 16:37
1

You can't resort a map, it's order is predefined (by default, from std::less on the key type). The easiest solution for your problem would be to create a std::multimap<int, string> and insert your values there, then just loop over the multimap, which will be ordered on the key type (int, the number of occurences), which will give you the order that you want, without having to define a predicate.

etarion
  • 16,935
  • 4
  • 43
  • 66
0

You are not going to be able to do this with one pass with an std::map. It can only be sorted on one thing at a time, and you cannot change the key in-place. What I would recommend is to use the code you have now to maintain the counters map, then use std::max_element with a comparison function that compares the second field of each std::pair<string, int> in the map.

Jeremiah Willcock
  • 30,161
  • 7
  • 76
  • 78
0

A map has its keys sorted, not its values. That's what makes the map efficent. You cannot sort it by occurrences without using another data structure (maybe a reversed index!)

Benoit
  • 76,634
  • 23
  • 210
  • 236
0

As stated, it simply won't work -- a map always remains sorted by its key value, which would be the strings.

As others have noted, you can copy the data to some other structure, and sort by the value. Another possibility would be to use a Boost bimap instead. I've posted a demo of the basic idea previously.

Community
  • 1
  • 1
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

You probably want to transform map<string,int> to vector<pair<const string, int> > then sort the vector on the int member.

You could do

struct PairLessSecond
{
  template< typename P >
  bool operator()( const P& pairLeft, const P& pairRight ) const
  {
    return pairLeft.second < pairRight.second;
  }
};

You can probably also construct all this somehow using a lambda with a bind.

Now

   std::vector< std::map<std::string,int>::value_type > byCount;
   std::sort( byCount.begin(), byCount.end(), PairLessSecond() );
CashCow
  • 30,981
  • 5
  • 61
  • 92