Using value from one map as key in another map in C++ - c++

I have a unordered_map AvailableRooms of hotels against number of rooms. Like
Sheridan -> 10
Marriot ->12
I have a map hotelOnDate which stores, string date converted to unix time against hotel.
142356789 -> Sheridan
142356749 -> Marriot
Now when I try to access the hotels unordered_map values using the map keys, I get a 0.
for(auto it = hotelOnDate.begin(); it != hotelOnDate.end(); it++){
std::cout<<AvailableRooms[it->second]<<std::endl;
}
AvailableRooms["Sheridan"] though gives the correct output 10.
What am I doing wrong here?

I tried the same thing in my code and seems to work for me.
#include <iostream>
#include <string>
#include <unordered_map>
#include <map>
int main ()
{
std::map<std::string,std::string> mymap = {
{ "Mars", "g"},
{ "Saturn", "h"},
{ "Jupiter", "i" } };
std::unordered_map<std::string,std::string> imap = {{"g","1"},{"h","2"}};
for (auto it = mymap.begin(); it != mymap.end(); it++) {
std::cout << it->first << ": " <<imap[ it->second ] << std::endl;
}
}
return 0;
}

Related

How to get the key of a map using the value in C++ STL

Get key by inputting the value of that key in C++ STL
map<int,int> m;
m[0]=8;
m[8]=7;
m[1562]=4;
m[100]=1;
auto i=m.find(1562);
cout<<endl<<i->first;
You cant. The map works by hashing the key and then using that to find the value stored in memory. It does not allow you to use the value to index the key.
What you can do is, iterate through the map to find the value and get the key from it.
int key = 0;
int value = 4;
for(auto entry : m)
{
if(entry.second == value)
{
key = entry.first;
break; // Exit from the loop.
}
}
Reference: cppreference
std::map is an ordered by key container. So to find a key by value you need to use the sequential search. For example you can use the standard algorithm std::find_if.
Here is a demonstrative program.
#include <iostream>
#include <map>
#include <iterator>
#include <algorithm>
int main()
{
std::map<int,int> m =
{
{ 0, 8 }, { 8, 7 }, { 1562, 4 }, { 100, 1 }
};
int value = 4;
auto it = std::find_if( std::begin( m ), std::end( m ),
[&value]( const auto &p )
{
return p.second == value;
} );
if ( it != std::end( m ) )
{
std::cout << it->first << ' ' << it->second << '\n';
}
return 0;
}
The program output is
1562 4
Or you should use a non-standard container that allows a quick access to elements of the container by key and by value.
You may do it like this as well:
#include<iostream>
#include <map>
int findByValue(std::map<int, int> mapOfElemen, int value)
{
std::map<int, int>::iterator it = mapOfElemen.begin();
// Iterate through the map
while(it != mapOfElemen.end())
{
// Check if value of this entry matches with given value
if(it->second == value)
{
return it->first;
}
// Go to next entry in map
it++;
}
//key for the value is not found hou probably need to change this depending on your key space
return -9999;
}
int main()
{
std::map<int,int> m;
m[0]=8;
m[8]=7;
m[1562]=4;
m[100]=1;
int value = 1562;
int result = findByValue( m, value);
std::cout << "key for "<< value << " is " << result << std::endl;
value = 8;
result = findByValue( m, value);
std::cout << "key for "<< value << " is " << result << std::endl;
value = 7;
result = findByValue( m, value);
std::cout << "key for "<< value << " is " << result << std::endl;
}
The result would give this:
key for 1562 is -9999
key for 8 is 0
key for 7 is 8
key for 4 is 1562
You can use an unordered map:
#include <iostream>
#include <unordered_map>
using namespace std;
int main()
{
unordered_map<int,int> m = {{0,8},{8,7},{1562,4},{100,1}};
cout<<m[1562];
return 0;
}
This will print the output as 4.

Efficient way to get key from value when map contain vector of string as value

How to get key using value which is vector of string and vice versa. Below is my code.
#include<iostream>
#include<map>
#include<string>
#include <unordered_map>
#include <vector>
using namespace std;
int main()
{
std::unordered_map<std::string, std::vector<std::string>> Mymap;
Mymap["unique1"] = {"hello", "world"};
Mymap["unique2"] = {"goodbye", "goodmorning", "world"};
Mymap["unique3"] = {"sun", "mon", "tue"};
for(auto && pair : Mymap) {
for(auto && value : pair.second) {
std::cout << pair.first<<" " << value<<"\n";
if(value == "goodmorning") // how get key i.e unique2 ?
}}
}
case 1: When value is input. key is output.
Input : goodmorning
output : unique2
case 2: When key is input value is output.
Input : unique3
output: sun ,mon ,tue
Note : No boost library available.
For case 1, a combination of find_if and any_of will do the job.
For case 2, you can simply use the find method of unordered_map.
#include<iostream>
#include<map>
#include<string>
#include <unordered_map>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
unordered_map<string, vector<string>> Mymap;
Mymap["unique1"] = { "hello", "world" };
Mymap["unique2"] = { "goodbye", "goodmorning", "world" };
Mymap["unique3"] = { "sun", "mon", "tue" };
// Case 1
string test_value = "goodmorning";
auto iter1 = find_if(Mymap.begin(), Mymap.end(),
[&test_value](const decltype(*Mymap.begin()) &pair)
{
return any_of(pair.second.begin(), pair.second.end(), [&test_value](const string& str) { return str == test_value; });
});
if (iter1 != Mymap.end())
{
cout << "Key: " << iter1->first << endl;
}
else
{
cout << "No key found for " << test_value;
}
// Case 2
test_value = "unique3";
auto iter2 = Mymap.find(test_value);
if (iter2 != Mymap.end())
{
int first = true;
for (auto v : iter2->second)
{
cout << (first ? "" : ", ") << v;
first = false;
}
cout << endl;
}
else
{
cout << "No value found for key " << test_value << endl;
}
return 0;
}
The key is stored in pair.first. Just use that if your use-case is in loop iteration as you illustrated.
If you mean in any use, without iteration, that is, given a value obtain the associated key, there is not a direct way to do that. You could build inverse maps for each value to key but that would not be really efficient considering also the fact that you would also need unique values.
Create another map going the other way for every vector entry?
If the array entries are not unique, then you would need to do the same map-to-vector, or use multimap.
Also consider using hash map (unordered_map), and stringview as ways to reduce the memory usage of the second map?
But the best answer would be the boost 2-way map, sorry. You could wrap the two maps in your own class that exposes the functionality of a 2-way map.

How to store different character's positon using vector or map

I have a string like "aabcdba" now I want to store the position of different character's position. I am trying to store using vector and unordered_map. Is there any good approach to store the position of different characters?
void topKFrequent(string s) {
vector<vector<int> >v(123);
//unordered_map<char, vector<int>>m;
for(int i=0;i<s.size();i++) {
v[s[i]].push_back(i);
// m[s[i]].push_back(i);
}
for(int i=0;i<123;i++) {
for(int j=0;j<v[i].size();j++) {
char ch=i;
cout<<ch<<"->"<<v[i][j]<<endl;
}
}
}
if string = "aabcdba", I want the following result:
a->0,1,6;
b->2,5;
c->3;
d->4;
You could use a map<char, vector<unsigned int> >.
#include <iostream>
#include <map>
#include <string>
#include <vector>
using namespace std;
map<char, vector<unsigned int> > storePos(string s)
{
map<char, vector<unsigned int> > charPos;
for(int i=0;i<s.size();i++)
{
auto itr = charPos.find(s[i]);
if(itr != charPos.end())
{
itr->second.push_back(i);
}
else
{
charPos[s[i]] = vector<unsigned int>(1, i);
}
}
return charPos;
}
int main(void)
{
string example = "aabcdba";
auto result = storePos(example);
for(auto itr1 = result.begin(); itr1 != result.end(); itr1 ++)
{
cout << "Letter: " << itr1->first << ", Locations: ";
for(auto itr2 = itr1->second.begin(); itr2 != itr1->second.end();
itr2 ++)
{
cout << *itr2 << " ";
}
cout << endl;
}
}
If you really want to store ordinal positions in the original string sequence, you can do so with either an unordered or ordered map of char to vector, where char is the key, and the vector contains the positions. Using an unordered map will not give you the lexicographical ordering of keys you seem to be seeking, but will nonetheless give you accurate positional vectors.
#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
int main()
{
std::string s = "aabcdba";
std::unordered_map<char, std::vector<unsigned int>> mymap;
for (unsigned i=0; i<s.size(); ++i)
mymap[s[i]].push_back(i);
for (auto const& pr : mymap)
{
std::cout << pr.first << "->";
auto it = pr.second.cbegin();
std::cout << *it;
while (++it != pr.second.cend())
std::cout << ',' << *it;
std::cout << ";\n";
}
}
Output
d->4;
c->3;
b->2,5;
a->0,1,6;
If you want lexicographical ordering, the simplest alternative is to simply using a regular ordered map instead. Changing only this:
std::unordered_map<char, std::vector<unsigned int>> mymap;
to this:
std::map<char, std::vector<unsigned int>> mymap;
and including the appropriate header delivers us this for output:
a->0,1,6;
b->2,5;
c->3;
d->4;
which fits exactly what you seem to be looking for.
A possible implementation to store the positions could be using unordered_multimap: (where the key characters can be repeated).
void storePos(string s) {
unordered_multimap<char, int>m;
for(int i=0;i<s.size();i++) {
m.insert(make_pair(s[i],i));
}
}
[EDITED]
But the output may depend on how you use it, or print out the data.
For example, consider the use of a std::multimap instead of std::unordered_map, to populate it you just do:
multimap<char, int>m;
void storePos(string s) {
for(int i=0;i<s.size();i++) {
m.insert(make_pair(s[i],i));
}
}
And to print the data you could have the following method:
void printPos()
{
std::multimap<char,int>::iterator it,itup;
for (it = m.begin(); it != m.end(); )
{
cout << (*it).first << " -> ";
itup = m.upper_bound ((*it).first );
// print range [it,itup):
for (it; it!=itup; ++it)
{
cout << (*it).second << ", ";
}
cout << endl;
}
}
Output:
a -> 0, 1, 6,
b -> 2, 5,
c -> 3,
d -> 4,
Try this!

How to change the key value of inner map in c++?

I'm working on map and I have following nested map and initialized with some values:
map<string, map<int, int> > wordsMap;
map<int, int> innerMap;
map<int, int>::iterator iti;
for(int i = 2; i < argc; i++)
{
wordsMap[argv[i]].insert(pair<int, int>(0,0));
}
And after some processing I'm trying to change the content if inner map, I use following code:
while(some_condition)
{
i = 0
for( it = wordsMap.begin() ; it != wordsMap.end(); it++)
{
innerMap = it->second;
int cnt = count(words.begin(), words.end(), it->first);
if(cnt != 0){
wordsMap[it->first][i] = cnt;
}
}
i++;
}
In the above scenario, How to change the value of first key (i.e. "0") and its value used while initialization of the inner map with another key-value pair?
You can't change the key of an element in an std::map. Doing so would break ordering.
Instead, you must insert a new element in the map with the key you want, and delete the previous element from the map.
I'm not sure if I understand your intend. I assume you want to save,
<KEY : file_name, VALUE : <KEY : line, VALUE : words count>>
And you don't want to save second map if there is no words.
So, I wrote below code.
If you want to not present second map, just keep empty map through not inserting key-value.
Additionally, sincestd::map is an associative container, which means it is saved sorted based on Key value, you should try to avoid change the key value after saving it.
#include "stdafx.h"
#include <algorithm>
#include <string>
#include <iostream>
#include <vector>
#include <map>
using namespace std;
typedef std::map<int, int> WORDS_COUNT_MAP_T; //for line, words count
typedef std::map<string, WORDS_COUNT_MAP_T> FILE_WORDS_COUNT_MAP_T; //for file name, WORDS_COUNT_MAP_T
int _tmain(int argc, _TCHAR* argv[])
{
FILE_WORDS_COUNT_MAP_T file_words_count_map;
//Input dummy data for test
//init file names
std::vector<string> file_names;
file_names.push_back("first");
file_names.push_back("second");
file_names.push_back("third");
//get and set words count in each file
for_each(file_names.begin(), file_names.end(), [&](const string& file_name)
{
//Just for test
WORDS_COUNT_MAP_T words_count_map;
if(file_name == "second")
{
//not find words, so nothing to do
}
else
{
words_count_map[0] = 10;
words_count_map[1] = 20;
}
file_words_count_map.insert(FILE_WORDS_COUNT_MAP_T::value_type(file_name, words_count_map));
});
//print
for_each (file_words_count_map.begin(), file_words_count_map.end(), [&](FILE_WORDS_COUNT_MAP_T::value_type& file_words_map)
{
cout << "file name : " << file_words_map.first << endl;
WORDS_COUNT_MAP_T words_count_map = file_words_map.second;
for_each (words_count_map.begin(), words_count_map.end(), [](WORDS_COUNT_MAP_T::value_type& words_map)
{
cout << "line : " << words_map.first << ", count : " << words_map.second << endl;
});
cout << "----" << endl;
});
getchar();
return 0;
}
This code will print like below,

can I put multimap iteration logic to another function?

I'm particularly interested for backward looping through keys with non repeating:
#include <map>
#include <iostream>
std::multimap<int,int> myMap = {
{1,2}, {1,2}, {2,2}, {2,2}, {3,2},
};
int main() {
using namespace std;
cout << "the keys backwards:" << endl;
typedef multimap<int, int> multimap_type;
typedef std::reverse_iterator<multimap_type::iterator> reverse_iterator;
for (auto it = myMap.rbegin(), end = myMap.rend(); it != end; it = reverse_iterator(myMap.lower_bound(it->first)))
{
cout << it->first << endl;
}
}
As you can see I must repeat multimap name three times among other things. Can I write my own function for handling all that and then call simply while or range for loop ? Like that:
while( (auto it = myIterFunc(myMap)) {
//...
}
for ( auto it : myIterFunc(myMap)) {
//...
}
for ( auto it : myIterFunc(myMap)) {
The names it and myIterFunc imply you are confused about the new range-based for loop. The variable it is not an iterator, it's an element of the range. The function myIterFunc should not return iterators, it should return something that looks like a range i.e. has begin() and end() members that allow iterating over the desired range.
You can use a Boost.Range adaptor to loop through it in reverse:
#include <boost/range/adaptors.hpp>
for (auto& val : boost::adaptors::reverse(myMap))
cout << val.first << endl;
You could combine that with a filter adaptor to skip over duplicate keys. (There is a uniqued adaptor but it uses == to determine uniqueness, instead of only inspecting keys)