Use a map with the map name defined by a string C++ - c++

This is a follow up question from Cout from a map with std::tuple
I have made a small map that I call BMW. It contains the keys Usage and Diesel, as shown below.
#include <iostream>
#include <bits/stdc++.h>
#include <map>
#include <vector>
using namespace std;
int main()
{
// initialize container
map<string, tuple<string, string>> BMW;
// insert elements
BMW.insert({"Usage", {"1", "2"}});
BMW.insert({"Disel", {"2", "3"}});
string sFirst_value;
string sSecond_value;
//prints out the map
for (const auto& x : BMW) {
sFirst_value.assign(get<0>(BMW.find(x.first)->second));
sSecond_value.assign(get<1>(BMW.find(x.first)->second));
cout << x.first << "\n" << "Min: " << sFirst_value << "\n" << "Max: " << sSecond_value << "\n" << "\n";
}
return 0;
}
Is there anyway I can call the name of the map, BMW, from a string instead of writing BMW.insert({"Usage", {"1", "2"}});? Like this:
stirng Mycar;
Mycar.insert({"Usage", {"1", "2"}});

To expand on Quentin's comment with a small example:
std::map<std::string, std::map<std::string, std::tuple<std::string, std::string>>> mapMap;
std::string myCar = "BMW";
std::map<std::string, std::tuple<std::string, std::string>> &myCarMap = mapMap[myCar];
myCarMap.insert({"Usage", {"1", "2"}});
//Or simply
auto &bmwMap = mapMap["BMW"];
bmwMap.insert({"Usage", {"1", "2"}});
}
Probably you can find better names than mapMap though ;)

Related

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 insert vector of integers into Key, Value of std::map

Goal: Read numerical text files into vectors and then add the vectors to key,value std::map so that I can reference them by the key name I have specified for them, later.
Thought this would be easy and I am surprised that I can't find an answer for this already on StackOverflow.
Result Expected:
Print1 = {100,200,500,600}
Print2 = {7890,5678,34567,3,56}
Print3["NameA"] = Print1
Print3["NameB"] = Print2
If my process is inefficient or going in the wrong direction, I would appreciate the pointers.
I keep getting Semantic Issue build fails and no viable conversion from pair <const basic_string>
Current Code:
#include <string.h>
#include <iostream>
#include <map>
#include <utility>
#include <vector>
const std::string& key(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.first;
}
const std::string& value(const std::pair<std::string, std::string>& keyValue)
{
return keyValue.second;
}
int main()
{
std::vector<int> print1;
std::ifstream inputFile("numbers.txt");
// test file open
if (inputFile)
{
double value;
// read the elements in the file into a vector
while ( inputFile >> value ) {
print1.push_back(value);
}
}
inputFile.close();
std::vector<int> print2;
std::ifstream inputFile2("numbers2.txt");
// test file open
if (inputFile2)
{
double value;
// read the elements in the file into a vector
while ( inputFile2 >> value ) {
print2.push_back(value);
}
}
inputFile2.close();
std::map<std::string, std::vector<int>> contacts;
contacts["alice"] = print1;
contacts["bob"] = print2;
std::vector<std::string> keys(contacts.size());
std::vector<int> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
std::cout << "Keys:\n";
copy(keys.begin(), keys.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
std::cout << "\n";
std::cout << "Values:\n";
copy(values.begin(), values.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
You can reference map element directly which will create an entry if doesn't exist, and just fill it from the file read loop:
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <string>
int main()
{
std::map<std::string, std::vector<int>> m;
int num;
auto &&alice = m["alice"];
std::ifstream if_alice("numbers1.txt");
while (if_alice >> num)
alice.push_back(num);
if_alice.close();
auto &&bob = m["bob"];
std::ifstream if_bob("numbers2.txt");
while (if_bob >> num)
bob.push_back(num);
if_bob.close();
// test
for (auto &&data : m)
{
std::cout << "Name: " << data.first << "\t";
for (int num : data.second)
std::cout << num << " ";
std::cout << "\n";
}
}
First of all, there is no point in arguing that Xcode didn't show any error msgs for your code. Try either turning on all the compiler warnings or try in online compilers. The result will be not disappointing: https://godbolt.org/z/cU54GX
If I have understood correctly, you want to store your information from two files(the integer values) in a std::map, where its key = std::string and value = vector of integer array.
If so,
1. You have your problem starting from reading integers from files.
There you are using double for no reason and storing to
std::vector<int> (i,e print1 and print2).
2. Secondly, what if your file has not been open?. inputFile.close();
and inputFile2.close(); will close it anyway, without knowing the
fact. This is wrong.
The proper way would be:
inputFile.open("numbers.txt", std::ios::in); // opening mode
if (inputFile.is_open()) {
// do stuff
inputFile.close(); // you need closing only when file has been opened
}
3. If your intention is to only print keys and values, you don't
need to parse them to different vectors.
You can do it directly:
for(const std::pair<kType, vType>& mapEntry: contacts)
{
std::cout << "Key: " << mapEntry.first << " Values: ";
for(const int values: mapEntry.second) std::cout << values << " ";
std::cout << std::endl;
}
In c++17 you can use Structured binding
for(const auto& [Key, Values]: contacts)
{
std::cout << "Key: " << Key << " Values: ";
for(const int value: Values) std::cout << value << " ";
std::cout << std::endl;
}
4. If you really want to parse them to a different vector; first of all, the data structure for storing keys is wrong:
std::vector<int> values(contacts.size());
^^^^^^
which should have been a vector of vector of integers, as your vType = std::vector<int>. That is,
std::vector<std::vector<int>> values
^^^^^^^^^^^^^^^^^^
Secondly, you have the functions key(const std::pair<std::string, std::string>& keyValue) and value(const std::pair<std::string, std::string>& keyValue) wrong, where you are passing a pair of strings as keys and values.
This should have been
typedef std::string kType; // type of your map's key
typedef std::vector<int> vType;// value of your map's value
std::pair<kType, vType>
However, you can simply replace with lambdas, which would be more intuitive, in the sense of having the functions next to the line where you needed. For example,
std::vector<kType> keysVec;
keysVec.reserve(contacts.size());
auto getOnlyKeys = [](const std::pair<kType, vType>& mapEntry){ return mapEntry.first; };
std::transform(contacts.begin(), contacts.end(), std::back_inserter(keysVec), getOnlyKeys);
See an example code here

Nested Map Structure Create Empty Map

I'm don't get this to work. I want to initialize the following structure for later use.
map<pair<string,string>, map<string,vector<fs::path>>> filelist;
Thats means I provide the string pair for the first map. (e.g., "id1","id2") with an empty second map that I can fill in later. So I want to do something like this:
filelist.insert(
pair<pair<string,string>, pair<string,vector<fs::path>>>
(make_pair("id1","id2), **empty map??**));
Obviously when when I stick to the vector instead of the nested map I can do this:
filelist.insert(pair<pair<string, string>, vector<vector<fs::path>>>
(make_pair("id1","id2"),{}) );
But how can I initialize an empty map? Or is there an alternative data structure? Thanks
I want to initialize the following structure for later use.
map<pair<string,string>, map<string,vector<fs::path>>> filelist;
You can use operator[] to assign it.
Some thing like follows.
filelist[ std::make_pair("string1", "string2") ] = value();
where, using value = std::map< std::string, std::vector<int>>;
See live action: https://www.ideone.com/lxQir7
is there an alternative data structure?
A suggestion to reduce the complexity of your chosen data-structure is possible, only when you explain your problem and data manipulation requirements more in detail.
#include <iostream>
#include <map>
#include <string>
#include <vector>
using key = std::pair< std::string, std::string>;
using value = std::map< std::string, std::vector<int>>;
int main ()
{
std::map< key , value > filelist;
value vMap;
vMap["string"] = {1,2,3,4};
filelist[ std::make_pair("string1", "string2") ] = vMap;
// to print or access
for(auto& key_itr: filelist)
{
std::cout<< "KEY: " << key_itr.first.first << " " << key_itr.first.second << "\t VALUE: ";
for(auto& val_itr: key_itr.second)
{
std::cout << val_itr.first << " ";
for(const auto& vec: val_itr.second) std::cout << vec << " ";
}
std::cout << std::endl;
}
return 0;
}
You can initialize an empty map, simply by its default constructor.
It is always a better idea to name your newly introduced data types.
After all, it's a relatively complex data structure. Why don't you simplify your problem?
#include <map>
#include <vector>
#include <filesystem>
using namespace std;
using namespace std::filesystem;
using string_to_vector_of_path_map = map<string, vector<path>>;
using pair_of_strings = pair<string, string>;
using my_map = map<pair_of_strings, string_to_vector_of_path_map>;
my_map filelist;
int main()
{
filelist.insert(make_pair(make_pair("id1", "id2"), string_to_vector_of_path_map()));
return 0;
}

Boost Bimap to insert_or_modify

STL map "[]" operator can insert new entries or modify existing entries.
map<string, string> myMap;
myMap["key1"] = "value1";
myMap["key1"] = "value2";
I am rewriting some code with boost::bimap which was implemented by STL map. Is there an easy way to keep the STL "[]" behavior? I found I have to write below 7 lines code to replace the original STL map code (1 line!).
bimap<string, string>::left_iterator itr = myBimap.left.find("key1");
if (itr != myBimap.left.end()) {
myBimap.left.replace_data(itr, "value2");
}
else {
myBimap.insert(bimap<string, string>::value_type("key1", "value2"));
}
I was wondering if there's an utility function like boost::bimap::insert_or_modify().
The Boost.Bimap documentation shows how mimic a std::map including its operator[] by using set_of and list_of for the bimap template arguments:
#include <iostream>
#include <string>
#include <boost/bimap.hpp>
#include <boost/bimap/set_of.hpp>
#include <boost/bimap/list_of.hpp>
int main()
{
using namespace std;
map<string, string> myMap;
myMap["key1"] = "value1";
myMap["key1"] = "value2";
for (auto&& elem : myMap)
std::cout << "{" << elem.first << ", " << elem.second << "}, ";
std::cout << "\n";
using namespace boost::bimaps;
bimap<set_of<string>, list_of<string>> myMap2;
myMap2.left["key1"] = "value1";
myMap2.left["key1"] = "value2";
for (auto&& elem : myMap2.left)
std::cout << "{" << elem.first << ", " << elem.second << "}, ";
std::cout << "\n";
auto res1 = myMap2.left.find("key1");
std::cout << "{" << res1->first << ", " << res1->second << "} \n";
}
Live Example.
UPDATE: the above code also allows left-searches. However, a right-search is not possible in combination with the required operator[] syntax. The reason is that operator[] modifications can only be done with a mutable right-view (such list_of or vector_of). OTOH, right-searches can only be done from immutable set_of and unordered_set_of and their multi- cousins.

map and unordered_map containing pointers to double?

If I have a std::map and std::unordered_map how could I use pointers on the double so that when the unordered_map updates the double value for a particular key, this is already reflected in the std::map for the same "key"?
So:
unordered_map["1"] = 6 causes map["1"] to be 6 also....
There's no reason why you can't use pointers.
Example:
#include <iostream>
#include <map>
#include <unordered_map>
#include <memory>
int main()
{
std::unordered_map<std::string, std::shared_ptr<double>> umap;
std::map<std::string, std::shared_ptr<double>> omap;
std::shared_ptr<double> value(new double(1234.5));
umap.emplace("key", value);
omap.emplace("key", value);
std::cout << "umap " << *umap["key"] << "\n";
std::cout << "omap " << *omap["key"] << "\n";
*umap["key"] = 9999.1;
std::cout << "omap " << *omap["key"] << "\n";
}
Output:
umap 1234.5
omap 1234.5
omap 9999.1
Maybe like this:
std::unordered_map<std::string, double> um;
std::unordered_map<std::string, double*> om;
om["1"] = &um["1"];
From now on, *om["1"] is always the value of the corresponding element in um. Just make sure you never delete elements from the unordered map.
(Source: iterator and reference invalidation rules)