I can sort an unordered map by value in descending order using this answer.
However, using a set for the same job fails:
#include <set>
#include <functional>
#include <iostream>
using namespace std;
typedef pair<string, int> Pair;
typedef function<bool(Pair, Pair)> Comparator;
Comparator DescendingSortComparator = [](Pair pair1, Pair pair2) {
return pair1.second > pair2.second;
};
void SortHashTableByValueDescending(unordered_map<string, int> hashTable) {
set<Pair, Comparator> orderedSet(hashTable.begin(), hashTable.end(), DescendingSortComparator);
for (auto element : orderedSet)
cout << element.first << ": " << element.second << endl;
}
Running with the following test:
void Test_SortMap()
{
unordered_map<string, int> CountTable;
CountTable["word"] = 1;
CountTable["spark"] = 15;
CountTable["the"] = 2;
CountTable["mail"] = 3;
CountTable["info"] = 3;
CountTable["sandwich"] = 15;
SortHashTableByValueDescending(CountTable);
}
yiels the following output:
spark: 15
info: 3
the: 2
word: 1
Can anyone please tell me why set (probably) overwrites pairs with same value? The keys for such pairs are distinct anyways.
See the definition of Compare function of std::set.
Everywhere the standard library uses the Compare concept, uniqueness is determined by using the equivalence relation. In imprecise terms, two objects a and b are considered equivalent if neither compares less than the other: !comp(a, b) && !comp(b, a).
This means that equal numbers will considered equivalent and not copied into your orderedSet
Use
Comparator DescendingSortComparator = [](Pair pair1, Pair pair2) {
if (pair1.second == pair2.second)
return pair1.first > pair2.first;
else return pair1.second > pair2.second;
};
if you want to keep them
From the cppreference.com:
std::set is an associative container that contains a sorted set of
unique objects of type Key.
According to your Comparator only a single std::pair with a fixed second element can be stored in the set.
Related
I need to get the pairs of the map sorted by its values, i wonder if it is posible without an temporal declaration.
I know i can sort it if i make another map with the keys and values swaped, but i am searching for a better solution.
I can't sort the elements afterwards because i only need extract the chars and put them on an array for example.
std::map<char,int> list = {{'A',4},{'V',2},{'N',1},{'J',5},{'G',3}};
for(/* code here */){
std::cout << /* code here */ << std::endl;
}
Desired outout:
J 5
A 4
G 3
V 2
N 1
This cannot be done with std::map.
This template has an optional template argument which allows for a custom sorting, but this sorting can only be done on the map's key:
template<
class Key,
class T,
class Compare = std::less<Key>,
class Allocator = std::allocator<std::pair<const Key, T> >
> class map;
std::map is a sorted associative container that contains key-value pairs with unique keys. Keys are sorted by using the comparison function Compare.
This choice has been done as to not impose the map's value to be an orderable type.
As an alternative, you can use type std::vector<std::pair<char, int>> in combination with std::sort.
#include <vector>
#include <utility>
#include <algorithm>
#include <iostream>
int main()
{
std::vector<std::pair<char,int>> list = {{'A',4},{'V',2},{'N',1},{'J',5},{'G',3}};
std::sort(begin(list), end(list), [](auto lhs, auto rhs) {
return lhs.second > rhs.second ? true : ( rhs.first > rhs.first );
});
for(auto const& pair : list) {
std::cout << pair.first << ", " << pair.second << '\n';
}
}
J, 5
A, 4
G, 3
V, 2
N, 1
Live demo
The most appropriate solution depends on HOW are you going to use that list. Is it created once and queried once? Then anything will do: you can just sort the output.
However, if this is a "live" list that is constantly updated, and you need to query it frequently, I would suggest to keep another map of values to vector of keys. Then you would be able to get your result instantly. At a cost
of more expensive insertion, of course.
I wanted the elements of the map to be arranged in a specific sequence(shortest first).
So i wrote a simple comparator which compares the length of the elements being inserted in the map with the previous element(s).
struct cmpByStringLength {
bool operator()(const std::string& a, const std::string& b) const {
return a.length() < b.length();
}
};
int main()
{
map<string,string,cmpByStringLength> obj1;
obj1.insert(make_pair("Anurag","Last"));
obj1.insert(make_pair("Second","Last"));
for(map<string,string>::iterator it=obj1.begin();it!= obj1.end();++it)
{
cout<<it->first;
cout<<endl;
}
return 0;
}
But the above won't insert element with key as Second in the map since the comparator compares elements with keys Second with Anurag and they have equal length so doesn't insert element with key Second. However the following would work fine:
obj1.insert(make_pair("abc","Last"));
obj1.insert(make_pair("abcdefg","Last"));
obj1.insert(make_pair("abcd","Last"));
Turns out, my understanding about custom comparator to sort the elements in a map is wrong as it is used for inserting the elements and not for inserting it as per the sort logic i provided through comparator.
So in other words, is it correct to say that custom comparators are just used for deciding whether or not to insert an element at all in the map, and it is not used to decide where to place the element?
C++ documentation on map (and really anything using a std::less-style comparator) makes it quite clear that two elements a, b are equivalent iff !comp(a, b) && !comp(b, a) (see, for example, https://en.cppreference.com/w/cpp/container/map). This means that, yes, your comparator gets used for ordering and equivalence testing.
The way you'd usually fix this is to implement a two-level comparison, e.g.
return (a.length() == b.length()) ? (a < b) : (a.length() < b.length());
Using std::multimap can solve your problem. But I don't know size of your data so I can't comment how it will effect performance on your application.
#include <iostream>
#include <map>
using namespace std;
struct cmpByStringLength {
bool operator()(const std::string &a, const std::string &b) const {
return a.length() < b.length();
}
};
int main() {
multimap<string, string, cmpByStringLength> obj1;
obj1.insert(make_pair("Second1", "Last"));
obj1.insert(make_pair("Anurag", "Last"));
obj1.insert(make_pair("Second", "Last"));
obj1.insert(make_pair("Secon", "Last"));
for (map<string, string>::iterator it = obj1.begin(); it != obj1.end();
++it) {
cout << it->first;
cout << endl;
}
return 0;
}
Output
Secon
Anurag
Second
Second1
I'm trying to sort a map by value. I did a research about how to do it and ended up with the following code. However, it will not compile and I am not sure why.
#include <iostream>
#include <map>
#include <algorithm>
#include <iterator>
using namespace std;
bool cmp(pair<int,int> const & a, pair<int,int> const & b)
{
return a.second != b.second? a.second < b.second : a.first < b.first;
}
int main()
{
map<int,int>myMap;
for(int i=0,j=10;i<10;i++,j--)
{
myMap.emplace(i,j);
}
for(map<int,int>::iterator it=myMap.begin();it!=myMap.end();it++)
{
cout << it->first << " " << it->second << endl;
}
sort(myMap.begin(),myMap.end(),cmp);
for(map<int,int>::iterator it=myMap.begin();it!=myMap.end();it++)
{
cout << it->first << " " << it->second << endl;
}
return 0;
}
The another (technical) possible cause of errors (aside from that it is nonsense semantically and that map iterators are not random, as #myaut said) is map (and unordered_map) underlying value type. In your case it is actually std::pair<const int, int>. So:
Your comparison function will not accept it (it will try to bind reference to wrong type)
Even if you fix it, sort will try to move values around, assigning them. And you cannot assign to the const variable
std::map is always sorted by keys. You need to convert to list of pairs first then sort list. Speaking of which, std::sort requires random iterators according to its prototype:
template< class RandomIt >
void sort( RandomIt first, RandomIt last );
Random iterators mean that std::sort should be able to access element at any index (i.e. for swapping elements 1 and 3). However, index access doesn't have any sense for map because it is accessed by keys, not indexes.
Example:
std::list<std::map<int,int>::value_type> list;
std::copy(myMap.begin(), myMap.end(), std::back_inserter(list));
list.sort(cmp);
I have written a bit of code to match two vector of objects that have some of the same instances of objects within both of the vectors.
The idea is to find the index of the object in the 'main' vector and match that to the object of the other vector.
The index of the main vector would then be used in a map with that object.
I think looking at the code may make my explanation a bit clearer:
ifndef OBJECTMAPMATCH_H
#define OBJECTMAPMATCH_H
#include <map>
#include <utility>
#include <vector>
#include <typeinfo>
#include <iostream>
#include <stdlib.h>
namespace ObjectMapMatch {
...
...
template< class A, class B >
std::map<int, B*>* getIndexMap( std::vector<A*>* x , std::vector<B*>* y, std::map<int, B*>* output )
{
typename std::vector<A*>::iterator Aitr = x->begin();
typename std::vector<A*>::iterator AitrE = x->end();
typename std::vector<B*>::iterator Bitr = y->begin();
typename std::vector<B*>::iterator BitrE = y->end();
for(int index=0; Aitr!=AitrE; ++Aitr, ++index){
//Keep track of original index
int AntupIndex = (*Aitr)->Index();
int match = false;
for(; Bitr!=BitrE; ++Bitr){
int BntupIndex = (*Bitr)->Index();
if( AntupIndex == BntupIndex ){
match = true;
output[index] = (*Bitr);
}
} //End of loop B
if(!match){
std::cout << "ERROR:ObjectMapMatch::getIndexMap: Can not Find Match" << typeid(y).name() << " FOR " << typeid(x).name() << std::endl;
exit(1);
}
}//End of Loop A
}
...
...
}
#endif
As you can see I am basically comparing the two objects with there unique index and if this matches the object will match.
My quesitons:
I know I could have overloaded the comparison operator in the object class, but I was not sure if something like this would be correct??
bool operator==(object1& lhs, object2& rhs){
&lhs == &rhs ? return true : return false;
}
Also,
Is there a shorter/more efficient way of the above code using some STL algorithms (can not use the boost libs) or something smarter??
Mike
There are couple of ways to do this more efficiently than O(n^2).
For example:
Sort elements of the first vector.
Sort elements of the second vector.
Use set_intersection on sorted vectors.
Or:
Put elements of both vectors to multiset (or unordered_multiset).
Keys of the multiset that have more than one element indicate a match.
For both of these methods, you could use pointers or indexes in original vectors instead of the actual elements. Just be careful to provide comparer (to sort, set_intersection and multiset) able to deal with pointers/indexes.
I know find method finds the supplied key in std::map and return an iterator to the element. Is there anyway to find the value and get an iterator to the element? What I need to do is to check specified value exist in std::map. I have done this by looping all items in the map and comparing. But I wanted to know is there any better approach for this.
Here is what I have wrote
bool ContainsValue(Type_ value)
{
bool found = false;
Map_::iterator it = internalMap.begin(); // internalMap is std::map
while(it != internalMap.end())
{
found = (it->second == value);
if(found)
break;
++it;
}
return found;
}
Edit
How about using another map internally which stores value,key combination. So I can call find on it? Is find() in std::map doing sequential search?
Thanks
You can use boost::multi_index to create a bidirectional map - you can use either value of the pair as a key to do a quick lookup.
If you have access to the excellent boost library then you should be using boost::multi_index to create bidirectional map as Mark says. Unlike a std::map this allows you to look up by either the key or the value.
If you only have the STL to hand the following code will do the trick (templated to work with any kind of map where the mapped_type supports operator==):
#include <map>
#include <string>
#include <algorithm>
#include <iostream>
#include <cassert>
template<class T>
struct map_data_compare : public std::binary_function<typename T::value_type,
typename T::mapped_type,
bool>
{
public:
bool operator() (typename T::value_type &pair,
typename T::mapped_type i) const
{
return pair.second == i;
}
};
int main()
{
typedef std::map<std::string, int> mapType;
mapType map;
map["a"] = 1;
map["b"] = 2;
map["c"] = 3;
map["d"] = 4;
map["e"] = 5;
const int value = 3;
std::map<std::string, int>::iterator it = std::find_if( map.begin(), map.end(), std::bind2nd(map_data_compare<mapType>(), value) );
if ( it != map.end() )
{
assert( value == it->second);
std::cout << "Found index:" << it->first << " for value:" << it->second << std::endl;
}
else
{
std::cout << "Did not find index for value:" << value << std::endl;
}
}
How about using another map internally which stores value,key combination. So I can call find on it?
Yes: maintain two maps, with one map using one type of key and the other using the other.
Is find() in std::map doing sequential search?
No it's a binary search of a sorted tree: its speed is O(log(n)).
Look into boost's bidirectional maps: http://www.boost.org/doc/libs/1_38_0/libs/bimap/doc/html/index.html
It lets both values act like a key.
Otherwise, iteration is the way to go.
What you are requesting is precisely what std::find does (not the member function)
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
No, you have to loop over the std::map and check all values manually. Depending on what you want to do, you could wrap the std::map in a simple class that also caches all of the values that are inserted into the map in something that's easily search-able and doesn't allow duplicates, like a std::set. Don't inherit from the std::map (it doesn't have a virtual destructor!), but wrap it so that you can do something like this:
WrappedMap my_map< std::string, double >;
my_map[ "key" ] = 99.0;
std::set< double > values = my_map.values(); // should give back a set with only 99.0 in it
An alternative to rolling your own would be to use the Boost bidirectional map, which is easily found in the posts below or by Google.
It really depends on what you want to do, how often you want to do it, and how hard it is to roll your own little wrapper class versus installing and using Boost. I love Boost, so that's a good way to go - but there's something nice and complete about making your own wrapper class. You have the advantage of understanding directly the complexity of operations, and you may not need the full reverse mapping of values => keys that's provided by the Boost bidirectional map.
Not a very best option but might be useful in few cases where user is assigning default value like 0 or NULL at initialization.
Ex.
< int , string >
< string , int >
< string , string >
consider < string , string >
mymap["1st"]="first";
mymap["second"]="";
for (std::map<string,string>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
{
if ( it->second =="" )
continue;
}
I am adding this answer, if someone come here and looks for c++11 and above..
//DECLARE A MAP
std::map<int, int> testmap;
//SAMPLE DATA
testmap.insert(std::make_pair(1, 10));
testmap.insert(std::make_pair(2, 20));
testmap.insert(std::make_pair(3, 30));
testmap.insert(std::make_pair(4, 20));
//ELEMENTS WITH VALUE TO BE FOUND
int value = 20;
//RESULTS
std::map<int, int> valuesMatching;
//ONE STEP TO FIND ALL MATCHING MAP ELEMENTS
std::copy_if(testmap.begin(), testmap.end(), std::inserter(valuesMatching, valuesMatching.end()), [value](const auto& v) {return v.second == value; });
Possible that I don't fully understand what you're trying to accomplish. But to simply test whether or not a map contains a value, I believe you can use the std::map's built in find.
bool ContainsValue(Type_ value)
{
return (internalMap.find(value) != internalMap.end());
}