Nested Map Structure Create Empty Map - c++

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

Related

Use a map with the map name defined by a string 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 ;)

Composite map: take data from another Map

I need to take the occurrences of words taken from a file, using map<string,int>, and then I need to copy them to a map<int,
vector<string>, cmpDec >, and print them in decreasing order.
I tried to take word frequencies from a file to a map<string, int> and then I'm trying to copy it to a map<int,
vector<string> > with no results
I have declared 2 maps:
map<string, int> text;
map<int, vector<string>, cmpDec> freq;
I take the text from a file in the first map with the word frequencies:
while (rf >> words) {
text[words]++;
}
Now I have to put the frequencies in the second map (required), where I need to have first int, for num of word frequencies, vector with the words for each freq, and the compare for decreasing frequencies.
Now i'm trying to put the datas in the second map from the first in these ways:
map<string, int>::iterator iter_map1 = text.begin();
map<int, vector<string>>::iterator iter = freq.begin();
vector<string>::iterator iter_v;
for (; iter_map1 != text.end(); ++iter_map1) {
iter->first.insert(make_pair(iter_map1->second, iter->second.push_back(iter_map1->first)));
}
It gives 2 errors on the iter->second.... line:
...\BagOfWords.cpp|56|error: request for member 'insert' in 'iter.std::_Rb_tree_iterator<_Tp>::operator-><std::pair<const int, std::vector<std::__cxx11::basic_string<char> > > >()->std::pair<const int, std::vector<std::__cxx11::basic_string<char> > >::first', which is of non-class type 'const int'|
and
...\BagOfWords.cpp|56|error: invalid use of void expression|
What am I doing wrong? Is there an easier way to take words (and their frequencies) from a file and put them on the second map without passing from the first?
With C++17 you can do structured binding, which helps a lot when iterating through a map.
#include <map>
#include <vector>
#include <string>
#include <iostream>
using WordCounts = std::map<std::string, int>;
using FrequencyOfWords = std::map<int, std::vector<std::string>, std::greater<int>>;
int main()
{
WordCounts word_counts;
FrequencyOfWords words_freq;
std::vector<std::string> words = {"test", "hello", "test", "hello", "word"};
for(const auto& word : words)
word_counts[word]++;
for(const auto& [word, count] : word_counts)
words_freq[count].push_back(word);
for (const auto& [freq, words] : words_freq)
{
std::cout << "freq " << freq << " words";
for (auto const& word: words)
std::cout << " " << word;
std::cout << '\n';
}
}
I don't think that you can do this in one pass as you don't know the word counts upfront.
First, a couple of recommendations. Use typedef (or using for C++ 11 or later). This will save you some typing and also ensure that your types are correct. In your code freq and iter don't have the same underlying container type (they differ in the comparison used).
Secondly, try to use the standard library as much as possible. You don't show cmpDec but I guess that it is a comparator based on greater-than rather than the default less-than. I would prefer to see std::greater<int> rather than a custom comparator.
For your errors, in the line
iter->first.insert(...
iter is at the start of freq and you are trying to insert to first which is int.
This should probably be something like
freq[iter_map1->second].push_back(iter_map1->first);
Breaking that down
freq[iter_map1->second] This uses the int word count from text to lookup an entry in freq. If there is no entry an empty one will be inserted to freq.
.push_back(iter_map1->first) This inserts the string from text to the vector that was found or created in the previous step
Here is a full example of what I think you are trying to achieve.
#include <map>
#include <vector>
#include <string>
#include <functional>
#include <fstream>
#include <iostream>
using std::map;
using std::vector;
using std::string;
using std::greater;
using std::ifstream;
using std::cout;
using WordCounts = map<string, int>;
using FrequencyOfWords = map<int, vector<string>, greater<int>>;
int main()
{
WordCounts text;
FrequencyOfWords freq;
ifstream rf("so26.cpp");
string words;
while (rf >> words)
{
text[words]++;
}
WordCounts::const_iterator iter_map1 = text.begin();
for (; iter_map1 != text.end(); ++iter_map1)
{
freq[iter_map1->second].push_back(iter_map1->first);
}
for (auto const& e: freq)
{
cout << "freq " << e.first << " words";
for (auto const& w: e.second)
{
cout << " " << w;
}
cout << "\n";
}
}
Perhaps I misunderstood the question, but I reckon the following does what you want (I prefer unordered maps, as they are faster and you don't seem to need the ordering)
std::unordered_map<std::string,int> word_counts;
std::string word;
while(input >> word)
word_counts[word]++;
std::unordered_map<int,std::vector<std::string>> words_by_freq;
for(const auto& counted : word_counts)
words_by_freq[counted::second].push_back(counted::first);

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.

DynamicArray of struct - adding elements without creating struct variables/objects

I have defined struct with 3 UnicodeString values, so I can create struct variable like this:
someStruct x = {"value1","value2","value3"};
But what I want to do is a DynamicArray of my struct type and I want to add elements to this array without creating them earlier, but assigning struct values the same moment I add an array element.
I tried to do it this way
DynamicArray<someStruct> arrayOfSomeStruct;
arrayOfSomeStruct.Length = 1;
arrayOfSomeStruct[0] = {"value1","value2","value3"};
But it do not work this way. Would you help me with this?
EDIT:
I have found the solution that works but I'm not fully happy with it:
arrayOfSomeStruct[0].atr1 = "value";
arrayOfSomeStruct[0].atr2 = "value";
arrayOfSomeStruct[0].atr3 = "value";
Try to use vectors from the standard library
Vectors are sequence containers representing arrays that can change in size.
You can try the following code :
#include <iostream>
#include <vector>
#include <string>
using namespace std;
struct SomeStruct{
string str1;
string str2;
string str3;
};
int main()
{
vector< SomeStruct > someStructVector; //Create a vector of SomeStruct
someStructVector.push_back( {"str1 ", "str2 ", "str3"} ); //adds {"str1 ", "str2 ", "str3"} to the vector
for( auto ss : someStructVector )//Access the elements of the vector
cout << ss.str1 << ss.str2 << ss.str3 << endl;
return 0;
}

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)