custom comparison method for c++ map based on key and value - c++

I searched a lot but I didnt find the answer I want.
I need to sort c++ map based on keys and values.
something like this method :
bool mycomp(mii::iterator a, mii::iterator b) {
if (a->second > b->second)
return true;
else if (a->second < b->second)
return false;
else
return a->first > a->second;
}
and use it something like that
sort(m.begin() , m.end(), mycomp);
where m is :
map<int,int> m;
can I do such thing ? if yes, what should be the correct syntax.

When you define the std::map you can provide a Compare function as a template parameter. See the reference for more details.
However, I believe this is only for key sorting. Key-value sorting is not inherit to the map structure (i.e. what happens if the value changes?)

Related

What is wrong in replacing a m[nums[i]]=i with a m.insert() function in hash map, where m is the name of the hash map

This is a leetcode question: 219. Contains Duplicate II
I used the insert function for hash map. Can someone please tell me what is wrong with the first code?
//wrong solution
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_map<int,int>m;
int n=nums.size();
for(int i=0;i<n;i++){
if(m.find(nums[i])!=m.end() && abs(i-m[nums[i]]) <=k) return true;
m.insert(pair<int,int>(nums[i],i)); //this is the faulty part
}
return false;
}
};
//Right
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
unordered_map<int,int>m;
int n=nums.size();
for(int i=0;i<n;i++){
if(m.find(nums[i])!=m.end() && abs(i-m[nums[i]]) <=k) return true;
m[nums[i]]=i;
}
return false;
}
};
The std::map and std::unordered_map types have slightly unusual behavior for their insert functions. Specifically, if you use insert to add a key/value pair to the map, and the key already existed in the map, the insert operation doesn't actually add anything. Instead, it'll hand back an iterator to the element that was already there. Therefore, your first version of the code doesn't actually do what it looks like it does, since the insert call won't change the existing value.
On the other hand, using operator[] (the square brackets) doesn't do this. Selecting a key with operator[] either inserts a key/value pair and then returns a reference to the value, or returns a reference to the existing value. That's why the second version does work - it'll replace existing values with new ones when needed.

How to determine whether a value exists in map in C++

I understand that std::map is (Key, Value) pairs.
I want to search through the values of a map. Let us say that I want to find the highest value among the values in the std::map. How can I do that ?
For example let me consider a map like this:
John -> 100
Jeffrey -> 200
Krishna -> 147
I think it will be similar to this , but I am not sure.
for (auto it=m.begin(); it!=m.end(); it++)
{
if (it->second == 500)
{
cout << "Found";
}
else {
continue;}
}
Instead of iterating through std::map, is there any other inbuilt method using which I can check if a value exists in a std::map with O(1) time complexity ?
Q1: How to check if a value exists in hashmap?
You need to iterate through and check if such item exists. You can use `std::find_if() with a lambda or do that through a loop. If you do that quite often you may want to index values as well (see below)
Q2: How to iterate through all the values in a map and find the largest value or the smallest value ?
Again you iterate through container and find it or you can use std::max_element() or std::min_element() with a lambda as well. Though if you need to access values in sorted order you may consider to use boost::multimap which will allow to access data using hashed index by name and provide sorted or hashed index(es) for values, though you should be aware of a price of every index added.
For the second question, use:
std::map<std::string, int> foo = {{"John",100},{"Jeffrey",200},{"Krishna",147}};
std::cout << std::max_element(foo.begin(), foo.end(), [](const auto& p1, const auto& p2){return p1.second < p2.second;})->first;
std::cout << std::min_element(foo.begin(), foo.end(), [](const auto& p1, const auto& p2){return p1.second < p2.second;})->first;
Use an adapted lambda with std::find_if, you should be able to find also if a value exists in a map (or hash table).

How to return a value from std::map<int, std::string> with a uint64_t key type?

Novice question, but I searched for this and couldn't find something clearly solving my issue - apologies if this is obvious.
I have defined a map which looks like this:
map<int, string> testmap = {
{ 0, "a" },
{ 1, "b" },
{ 2, "c" }
}
However, I need to retrieve a value from testmap using a uint64_t value provided by another function.
When I do testmap[my_uint64_t_value] it returns an empty string, so I think this is because it's adding my_uint64_t_value as a key and setting the value to NULL.
This is the same if I set the map type to <uint64_t, string>, at least the way I'm currently defining my keys.
However, is there a way that I either:
convert the uint64_t value to a regular int
define the map as <uint64_t, string>, and be able to define my
keys as the 'correct' type?
It seems like int type conversion isn't that common, is this something that should be avoided?
The reason why you get an empty string is std::map::operator[] returns a reference to the value if and only if it exists, otherwise it performs an insertion. I suspect you have the latter case.
You need to use std::map::find for search.
uint64_t keyToFind = 1;
if (auto iter = testmap.find(keyToFind); iter != testmap.cend())
{
// do something
std::cout << "Found!\n";
}
else { std::cout << "Not Found!\n"; }
Like #Rene mentioned in the comments, casting from uint64_t to int can cause overflow. Therefore, making the key to larger type(as per requirement) would be a good idea.
std::map<uint64_t, std::string> testmap;
As said in another answer, the [] operator of the map class will perform an insertion with a default-constructed value if the key is not present in the map.
You can first use the count method to determine if the key is present in the map before accessing it.
if(testmap.count(keyToFind))
return testmap[keyToFind];
else
report_key_not_found();
An alternative solution is to use the at method to access the value. It will throw an std::out_of_range exception if the key is not present instead of inserting a new key.

C++ unordered_map where key is also unordered_map

I am trying to use an unordered_map with another unordered_map as a key (custom hash function). I've also added a custom equal function, even though it's probably not needed.
The code does not do what I expect, but I can't make heads or tails of what's going on. For some reason, the equal function is not called when doing find(), which is what I'd expect.
unsigned long hashing_func(const unordered_map<char,int>& m) {
string str;
for (auto& e : m)
str += e.first;
return hash<string>()(str);
}
bool equal_func(const unordered_map<char,int>& m1, const unordered_map<char,int>& m2) {
return m1 == m2;
}
int main() {
unordered_map<
unordered_map<char,int>,
string,
function<unsigned long(const unordered_map<char,int>&)>,
function<bool(const unordered_map<char,int>&, const unordered_map<char,int>&)>
> mapResults(10, hashing_func, equal_func);
unordered_map<char,int> t1 = getMap(str1);
unordered_map<char,int> t2 = getMap(str2);
cout<<(t1 == t2)<<endl; // returns TRUE
mapResults[t1] = "asd";
cout<<(mapResults.find(t2) != mapResults.end()); // returns FALSE
return 0;
}
First of all, the equality operator is certainly required, so you should keep it.
Let's look at your unordered map's hash function:
string str;
for (auto& e : m)
str += e.first;
return hash<string>()(str);
Since it's an unordered map, by definition, the iterator can iterate over the unordered map's keys in any order. However, since the hash function must produce the same hash value for the same key, this hash function will obviously fail in that regard.
Additionally, I would also expect that the hash function will also include the values of the unorderered map key, in addition to the keys themselves. I suppose that you might want to do it this way -- for two unordered maps to be considered to be the same key as long as their keys are the same, ignoring their values. It's not clear from the question what your expectation is, but you may want to think it over.
Comparing two std::unordered_map objects using == compares whether the maps contain the same keys. It does nothing to tell whether they contain them in the same order (it's an unordered map, after all). However, your hashing_func depends on the order of items in the map: hash<string>()("ab") is in general different from hash<string>()("ba").
A good place to start is with what hashing_func returns for each map, or more easily what the string construction in hashing_func generates.
A more obviously correct hash function for such a type could be:
unsigned long hashing_func(const unordered_map<char,int>& m) {
unsigned long res = 0;
for (auto& e : m)
res ^ hash<char>()(e.first) ^ hash<int>()(e.second);
return res;
}

Checking value exist in a std::map - C++

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());
}