This is a solution according to the intention described by OP.
Sample code:
#include <iostream>
#include <map>
#include <set>
#include <vector>
int main()
{
// Creating & Initializing a map of String & Ints
std::map<std::string, int> mapOfWordCount = {
{ "aaa", 10 }, { "ddd", 41 }, { "bbb", 62 }, { "ccc", 10 }
};
// auxiliary set of values
std::set<int> counts;
// creating a filtered map
std::vector<std::pair<std::string, int> > mapOfWordCountFiltered;
for (const std::map<std::string, int>::value_type &entry : mapOfWordCount) {
if (!counts.insert(entry.second).second) continue; // skip duplicate counts
mapOfWordCountFiltered.push_back(entry);
}
// output
for (const std::pair<std::string, int> &entry : mapOfWordCountFiltered) {
std::cout << "{ \"" << entry.first << "\", " << entry.second << " }\n";
}
// done
return 0;
}
Output:
{ "aaa", 10 }
{ "bbb", 62 }
{ "ddd", 41 }
Live Demo on coliru
There is no custom predicate used as the standard predicate (std::less<Key>) is sufficient for the solution (for map as well as set).
The filtered map doesn't even use a std::map as there is no necessity for this. (The entries are already sorted, the filtering is done by an extra std::set<int>.)
Actually, I have no idea how to perform this with a custom predicate as I don't know how to keep the (required) order of map with the extra check for duplicated values.
Isn't there a way to create a comparator that makes sure that another "key, value" is not inserted, if the value is already present in the map previously corresponding to a different key? This would save extra space that I would use by creating another set.
I have thought about this a while. Yes, it is possible but I wouldn't recommend it for productive code.
std::map::insert() probably calls std::map::lower_bound() to find the insertion point (i.e. iterator). (The std::map::lower_bound() in turn will use our custom predicate.) If the returned iterator is end() the entry is inserted at end. Otherwise, the key at this iterator is compared with the one which is provided as new (to be inserted). If it is equal the insertion will be denied otherwise the new entry is inserted there.
So, to deny insertion of an entry with duplicated value, the predicate has to return false regardless of comparison of keys. For this, the predicate has to do extra checks.
To perform these extra checks, the predicate needs access to the whole map as well as to the value of entry to be inserted. To solve the first issue, the predicate gets a reference to the map where it is used in. For the second issue, I had no better idea as to use a std::set<std::pair<std::string, int> > instead of the original std::map<std::string, int>. As there is already a custom predicate involved, the sorting behavior can be adjusted sufficiently.
So, this is what I got:
#include <iostream>
#include <map>
#include <set>
#include <vector>
typedef std::pair<std::string, int> Entry;
struct CustomLess;
typedef std::set<Entry, CustomLess> Set;
struct CustomLess {
Set &set;
CustomLess(Set &set): set(set) { }
bool operator()(const Entry &entry1, const Entry &entry2) const;
};
bool CustomLess::operator()(
const Entry &entry1, const Entry &entry2) const
{
/* check wether entry1.first already in set
* (but don't use find() as this may cause recursion)
*/
bool entry1InSet = false;
for (const Entry &entry : set) {
if ((entry1InSet = entry.first == entry1.first)) break;
}
/* If entry1 not in set check whether if could be added.
* If not any call of this predicate should return false.
*/
if (!entry1InSet) {
for (const Entry &entry : set) {
if (entry.second == entry1.second) return false;
}
}
/* check wether entry2.first already in set
* (but don't use find() as this may cause recursion)
*/
bool entry2InSet = false;
for (const Entry &entry : set) {
if ((entry2InSet = entry.first == entry2.first)) break;
}
/* If entry2 not in set check whether if could be added.
* If not any call of this predicate should return false.
*/
if (!entry2InSet) {
for (const Entry &entry : set) {
if (entry.second == entry2.second) return false;
}
}
/* fall back to regular behavior of a less predicate
* for entry1.first and entry2.first
*/
return entry1.first < entry2.first;
}
int main()
{
// Creating & Initializing a map of String & Ints
// with very specific behavior
Set mapOfWordCount({
{ "aaa", 10 }, { "ddd", 41 }, { "bbb", 62 }, { "ccc", 10 }
},
CustomLess(mapOfWordCount));
// output
for (const Entry &entry : mapOfWordCount) {
std::cout << "{ \"" << entry.first << "\", " << entry.second << " }\n";
}
// done
return 0;
}
Output:
{ "aaa", 10 }
{ "bbb", 62 }
{ "ddd", 41 }
Live Demo on coliru
My collaborator would call this a Frankenstein solution and IMHO this is sufficient in this case.
The intention of a std::map/std::set is usually an amortized insert() and find(). This effect is probably totally lost as the CustomLess must iterate (in worst case) over the whole set twice before a value can be returned. (The possible early-outs from iterations in some cases don't help much.)
So, this was a nice puzzle and I solved it somehow but rather to present a counter example.
std::maprequires a predicate to sort keys. YourcompFunctordoesn't match the type of required predicate. (This is probably the reason for compile error.) But even if this were fixed you would get run-time issues as a sufficient sorting of map keys would be missing. - Scheff's Catstd::map<int, string>for this. Assuming further, that values of first map are e.g. occurrences of words, the values are probably not unique. So, astd::multimapwould be more appropriate (or astd::map<int, std::vector<std::string> >). - Scheff's Cat