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);
Related
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 ;)
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.
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;
}
Is there any efficient way to count duplicates of each pair in a vector?
For example, if I have a vector like this:
vector<pair<int, int> > duplicates={{1,2},{3,2},{2,1},{5,6},{5,6},{1,2},{2,1},{5,6}};
The output should be:
{1,2}:2
{3,2}:1
{2,1}:2
{5,6}:3
And TO be CLEAR, I am just curious about how to solve this problem more efficiently. I have tried to compare each pair of this vector and it seems not a smart way.
An easy way is to use a map or unordered map to count them:
#include <iostream>
#include <vector>
#include <map>
int main( int argn, char **argc)
{
std::vector<std::pair<int, int> > duplicates={{1,2},{3,2},{2,1},{5,6},{5,6},{1,2},{2,1},{5,6}};
std::map<std::pair<int, int>, int> checker;
for (const auto &elem: duplicates)
{
++checker[elem];
}
for (const auto &elem: checker) std::cout << "{" << elem.first.first <<
"," << elem.first.second <<
"}: " << elem.second << std::endl;
return 0;
}
Note that map insertion/recovery is O(log(n)), and the loop around make it aprox. O(n*log(n))
EDIT:
Following the additional note of the OP, here is a better (faster) implementation using unordered_map:
#include <iostream>
#include <vector>
#include <unordered_map>
namespace std
{
template <>
struct hash<std::pair<int,int>>
{
size_t operator()(pair<int, int> const &p) const
{
// Fine for 64bit size_t and 32bit int. Otherwise, some collision may happens.
size_t result = (static_cast<size_t>(p.first) <<(sizeof(std::size_t)<<2))
+ static_cast<size_t>(p.second);
return result;
}
};
}
int main( int argn, char **argc)
{
std::vector<std::pair<int, int> > duplicates={{1,2},{3,2},{2,1},{5,6},{5,6},{1,2},{2,1},{5,6}};
std::unordered_map<std::pair<int, int>, int> checker;
for (const auto &elem: duplicates)
{
++checker[elem]; // value initialized with 0
}
for (const auto &elem: checker) std::cout << "{" << elem.first.first <<
"," << elem.first.second <<
"}: " << elem.second << std::endl;
return 0;
}
Insertion in unordered_map, using a hash make it usually constant (worse case when there are collision is linear). Final complexity in average is O(N)
I have a simple solution:
Sort vector of pairs
Then just a loop if match consecutive pairs then increase counter
General search Complexity: n*n
This search Complexity: nlog(n)
I have an std::map and I want to search for a key using a substring. For example, I have the following code:
#include <iostream>
#include <map>
#include <string>
using namespace std;
typedef std::map<std::string, std::string> TStrStrMap;
typedef std::pair<std::string, std::string> TStrStrPair;
int main(int argc, char *argv[])
{
TStrStrMap tMap;
tMap.insert(TStrStrPair("John", "AA"));
tMap.insert(TStrStrPair("Mary", "BBB"));
tMap.insert(TStrStrPair("Mother", "A"));
tMap.insert(TStrStrPair("Marlon", "C"));
return 0;
}
Now, I want to search for the position that holds the substring "Marl" and not "Marlon", if "Marla" is stored in the map. I want to find something that starts with "Marl". I need to find at most one position. Is this possible? If so, how?
I don't want to use any Boost libraries!
You can't efficiently search for substring, but you can for prefix:
#include <iostream>
#include <map>
#include <string>
#include <algorithm>
using namespace std;
typedef map<string, string> TStrStrMap;
typedef pair<string, string> TStrStrPair;
TStrStrMap::const_iterator FindPrefix(const TStrStrMap& map, const string& search_for) {
TStrStrMap::const_iterator i = map.lower_bound(search_for);
if (i != map.end()) {
const string& key = i->first;
if (key.compare(0, search_for.size(), search_for) == 0) // Really a prefix?
return i;
}
return map.end();
}
void Test(const TStrStrMap& map, const string& search_for) {
cout << search_for;
auto i = FindPrefix(map, search_for);
if (i != map.end())
cout << '\t' << i->first << ", " << i->second;
cout << endl;
}
int main(int argc, char *argv[])
{
TStrStrMap tMap;
tMap.insert(TStrStrPair("John", "AA"));
tMap.insert(TStrStrPair("Mary", "BBB"));
tMap.insert(TStrStrPair("Mother", "A"));
tMap.insert(TStrStrPair("Marlon", "C"));
Test(tMap, "Marl");
Test(tMap, "Mo");
Test(tMap, "ther");
Test(tMap, "Mad");
Test(tMap, "Mom");
Test(tMap, "Perr");
Test(tMap, "Jo");
return 0;
}
This prints:
Marl Marlon, C
Mo Mother, A
ther
Mad
Mom
Perr
Jo John, AA
When your substring is a prefix as in your example, you can use lower_bound to search for "Marl".
map<string,string>::const_iterator m = tMap.lower_bound("Marl");
cerr << (*m).second << endl;
This does not work for non-prefix substrings: in the general case, searching a map is not much different from searching other containers.
I'd like to expand on the answer by Sergey by providing a full solution using map::lower_bound(). As mentioned in the comments on that answer, you have to check whether lower_bound() returns tMap.end(). If not, then you also have to check whether the found key is actually prefixed with the search string. Latter can be checked, for example, by using string::compare(). As a result, my C++11 solution looks as follows:
std::map<std::string, std::string> myMap{
{"John", "AA"}, {"Mary", "BBB"}, {"Mother", "A"}, {"Marlon", "C"}, {"Marla", "D"}
};
std::string prefix("Marl");
auto it = myMap.lower_bound(prefix);
if (it != std::end(myMap) && it->first.compare(0, prefix.size(), prefix) == 0)
std::cout << it->first << ": " << it->second << std::endl;
Output:
Marla: D
However, if you want to find all keys in your map that are prefixed with the search string, then you can use the following loop:
for (auto it = myMap.lower_bound(prefix); it != std::end(myMap) && it->first.compare(0, prefix.size(), prefix) == 0; ++it)
std::cout << it->first << ": " << it->second << std::endl;
Output:
Marla: D
Marlon: C
Code on Ideone
To search for a substring of a key in a map you have no choice but to either use a new map on a special kind of key type or to search your map in O(n). std::map uses (by default) operator<() for ordering keys and for searching, and that compare function for std::string is a plain lexicographical compare.
If you create a new map on a special key type that has operator<() compare on basis of a substring take note that this will also affect the decision of whether a new element to insert would be a duplicate. In other words, such a map will only have elements that are not substrings of each other.
The O(n) search practically means you use std::find() over the map, with a custom predicate that takes a std::pair<std::string,std::string> and returns true if the second element of the pair is a substring of the first.
typedef TStrStrMap::value_type map_value_type;
struct key_contains_substring
: std::binary_function<map_value_type, std::string, bool>
{
bool operator()(const map_value_type& map_value, const std::string& substr)
{
return std::search(map_value.first.begin(), map_value.first.end(),
substr.begin(), substr.end()) != map_value.first.end();
}
};
...
TStrStrMap::const_iterator it = std::find_if(tMap.begin(), tMap.end(),
std::bind2nd(key_contains_substring(), "Marl");