Updating a map after every loop iteration in C++ 11 - c++

I have a loop, which is calculating the size of second member of the p,
where p is std::pair<const std::string, std::set<std::string>>
for (const auto& p : ref)
std::cout << p.second.size() << endl;
Now I want to create another map cnt (std::map<std::string, int> cnt;),
which saves p.first and p.second.size() after every iteration.
How can I do that?

Declare std::map<std::string, int> cnt;
Before you iterate through whatever data structure you are using to store the pairs. While you are iterating though this structure, simply put
cnt[p.first] = p.second.size();

Related

The best practice for (unordered) map keys and values modification

The map of the form map<long long, vector<long long>> is given. One has to take all keys and values modulo some integer N. Some keys can merge and corresponding values must join accordingly. For example, the map {{1,{2,6,4}}, {5,{8,4,9}}, {10,{5,1,7}}} should be equal to {{1,{2,1,4}}, {0,{0,1,2,3,4}}} after reduction modulo 5.
My way is in using a new map but I think there should be a better way.
code added
vector<long long> tmp;
//integer N, for example N = 5
int N = 5;
unordered_map<long long, vector<long long>> map;
//temporary map
unordered_map<long long, vector<long long>> map_tmp;
for (auto & x : map)
{
tmp.clear();
for (auto & y : x.second) tmp.push_back(y % N);
ind = x.first % N;
map_tmp[ind].insert(map_tmp[ind].end(), tmp.begin(), tmp.end());
sort(map_tmp[ind].begin(), map_tmp[ind].end());
map_tmp[ind].erase(unique(map_tmp[ind].begin(), map_tmp[ind].end()), map_tmp[ind].end());
}
map = map_tmp;
Since apparently values in map are unique and after applying modulo operation values contains unique items, then you should use different data structure. for example:
using Map = std::unordered_map<int, std::set<int>>;
std::set will handle uniqueness and order of items for given key.
Now the whole trick is to inspect API of std::unordered_map and std::set and how item can be inserted there. See:
std::unordered_map::insert
std::set::insert
Note return value: std::pair<iterator,bool> which gives you iterator to inserted or exciting item in map/set.
Knowing this thing writing a code which is able to meet your requriements is quite simple:
using Map = std::unordered_map<int, std::set<int>>;
Map moduloMap(const Map& in, int mod)
{
Map out;
for (const auto& [k, s] : in) {
if (s.empty())
continue;
auto& destSet = out.insert({ k % mod, {} }).first->second;
for (auto x : s) {
destSet.insert(x % mod);
}
}
return out;
}
Live demo with tests
Sometimes a for loop can be the easiest, clearest way to do something.
map<long long, vector<long long>> result;
for (const auto& [key, vec] : input) {
process (result[key%5], vec);
}
and process takes the vector by (non-const) reference and appends the reduced values from the second (const) argument.
update
After seeing the code you posted, I have several suggestions:
use a set instead. You are spending multiple steps to append the new values, sort the whole thing together, then remove duplicates. Just use a set which maintains a single copy of each value automatically.
use structured binding in your loop. Instead of x.second and x.first you can just name them key and vec as in my earlier post.
Assuming you still need tmp, declare it where you are calling .clear() now, instead of declaring it way up at the top of your code. You don't need to clear it each time through the loop; it will be empty each time through the loop naturally.

Push_back into map<int,vector<char>>*

c++
map<int, vector>* maxcounts;
When I have a pointer to map maxcount how do I write this next statement correctly?
maxcounts[-m.second]->push_back(m.first);
without referencing a pointer I write
maxcounts[-m.second].push_back(m.first);
map<int, vector<char>> maxcounts;
for (pair<char, int> m : counts) {
if (maxcounts.count(-m.second))
maxcounts[-m.second].push_back(m.first);
else
maxcounts.insert({ -m.second, {m.first} });
}
To figure out how to use a pointer to the map, first rewrite your loop this way:
std::map<char, int> counts;
//...
std::map<int, std::vector<char>> maxcounts;
for (std::pair<char, int> m : counts)
maxcounts.insert({-m.second, std::vector<char>()}).first->second.push_back(m.first);
Note that the return value for std::map::insert is a std::pair, where the first of the pair is an iterator to the existing item if the item already is in the map, or the iterator to the newly inserted item. Thus you can perform the test and insert in one line without need for an if statement.
The push_back will occur, regardless of whether the item inserted in the map is new or if the item existed. Note that for a new entry, the std::vector being inserted starts as empty.
Given this, the pointer to the map version is very simple:
std::map<char, int> counts;
//...
map<int, vector<char>>* maxcounts;
//
for (pair<char, int> m : counts)
maxcounts->insert({-m.second, std::vector<char>()}).first->second.push_back(m.first);
Now, why you need a pointer to a map in the first place is another issue, but to answer your question, the above should work.
I would likely write something like:
std::map<int, std::vector<int>>* maxcounts = ...;
for (std::pair<char, int> m : counts)
(*maxcounts)[-m.second].push_back(m.first);

How to get the top 100 names in a collection

I am creating a program where I am summarizing from a data file. The data file has information about first names, etc. The information are the fields in the csv file. The fields in the data file are included as instance variables in the class. I created setter and getter methods to return the data for one person. I created vectors to hold the collection of variables.
I am having trouble understanding how create a list of the 100 most common first names of all people in the collection. The list must be in descending order of occurrence.
I was able to print all the common names and its frequencies. But, I am unable to print the 100 most common names. I sorted the vector and got the following errors:
class std::pair<const std::string, int> has no member begin and end
Please help me resolve these issue. All processing of data in the vector must be done with iterators.I am not sure how to fix these issues since I am a beginner.
std::vector<std::string> commonNamesFirst; //vector
for (auto x : census) {
commonNamesFirst.push_back(x.getFirstName()); //populate vector
}
std::map<std::string, int> frequencies;
for (auto& x : census) { ++frequencies[x.getFirstName()]; }
for (auto& freq : frequencies) {
sort(freq.begin(), freq.end(), greater <>()); //error, need to sort in descending order
cout << freq.first << ": " << freq.second << endl; //print the 100 common names in descending order
}
std::map<std::string, int> frequencies;
This is generally the right direction. You're using this to count how many times each word occurs.
for (auto& freq : frequencies) {
This iterates over each individual word and a count of how many times it occured. This no longer makes any logical sense. You are looking to find the 100 most common ones, the one with the highest count values. Iterating, and looking at each one individually, in the manner that's done here, does not make any sense.
sort(freq.begin(), freq.end(), greater <>());
freq, here, is a single word and how many times it occured. You are using freq to iterate over all of the frequencies. Therefore, this is just one of the words, and its frequency value. This is a single std::pair value. And it does not have anything called begin, or end. And that's what your compiler is telling you, directly.
Furthermore, you cannot sort a std::map in the first place. This is not a sortable container. The simplest option is to extract the contents if the now-complete map into something that's sortable. Like, for example, a vector:
std::vector<std::pair<std::string, int>> vfrequencies{
frequencies.begin(),
frequencies.end()
};
So, you've now copied the contents of a map into a vector. Not the most efficient approach, but a workable one.
And now, you can sort this vector. Rather easily.
However, as one last detail, you can't just drop std::greater<> and expect the right thing to happen.
You are looking to sort on the frequency count value only, which is the .second of these std::pairs. A plain std::greater is not going to do this for you. The std::greater overload for a std::pair is not going to do what you think it will do, here.
You will need to provide your own custom lambda for the third parameter of std::sort, that compares the second value of the std::pairs in that vector.
And then, the first 100 most common words will be the first 100 values in the vector. Mission accomplished.
You cannot (re-)sort std::map, you can copy frequencies in vector or std::multimap as intermediate:
std::map<std::string, int> frequencies;
for (auto& x : census) { ++frequencies[x.getFirstName()]; }
std::vector<std::pair<std::string, int>> freqs{frequencies.begin(), frequencies.end()};
std::partial_sort(freqs.begin(), freqs.begin() + 100, freqs.end(),
[](const auto& lhs, const auto& rhs){ return lhs.second > rhs.second; });
for (std::size_t i = 0; i != 100; ++i)
{
std::cout << freqs[i].second << ":" << freqs[i] << std::endl;
}
Building on to #MichaƂ Kaczorowski's answer, you are trying to sort the values in each pair instead of the pairs in the map. However, as Sam mentoined, you cannot sort an std::map (the internal implementation stores things sorted by the key value, or the name in this case). You'd have to get the values out of the map and sort them then, or use a priority queue and heapsort (faster constant factor), or a monotonic queue (linear time but harder to implement). Here is an example heapsort implementation:
vector<string> commonNamesFirst; //vector
for (auto x : census) {
commonNamesFirst.push_back(x.getFirstName()); //populate vector
}
std::map<std::string, int> frequencies;
for (auto& x : census) { ++frequencies[x.getFirstName()]; }
std::priority_queue<pair<int, std::string> > top_names; // put the frequency before the name to take advantage of default pair compare
for (auto& freq : frequencies) top_names.push(std::make_pair(freq.second, freq.first));
for (int i=0; i<100; ++i)
{
outputFile << top_names.top().second << ": " << top_names.top().first << endl; //print the 100 common names in descending order
top_names.pop();
}
The error you get, says it all. You are trying to sort individual std::pair. I think the best way would be to transform your map into a std::vector of pairs and then sort that vector. Then just go through first 100 elements in a loop and print results.

Extract second element from a three element map in c++

I am creating a map. The map works fine though. I need to extract the character for a particular index (for another computation).
Code for map is below:
//site index, val type, num of val
typedef map<int, pair<char, int> > Maptype;
Maptype my_map;
for (i=0; i<SIZE; i++){ //create the system
char Type = 's';
int Count = 10;
//insert to map
my_map.insert( make_pair( i, make_pair(Type, i*Count)));
}
Now, I am trying to extract the character for a particular index. If I had two elements, I could've used the below one. But with three elements, I am not able to find the solution. Help please :)
for(auto &i: my_map)
cout << i.second << endl;
Iterating over my_map using auto gets you key-value pairs. In your case, the key is an int, the value is yet another std::pair.
i.second is the value of the key-value pair, so since it's a std::pair too, simply do i.second.first to get the value of Type and i.second.second for i*Count.
You might want to consider using const auto &i here instead, since you don't modify anything and don't intend to, as it seems. Also, std::endl flushes the stream buffer, which might be expensive if done in a loop. Just use '\n'.
i.second will be of type pair<char, int>& in your loop.
So in order to access the integer, simply use cout << i.second.second << endl;.

Get distinct vector of vector with count

What is the best way to return only the unique element with the counts from vector of vectors?
std::vector<std::vector<string>> vec_vec{{a,a,b,c},{a,c,c}};
The results should be :
{a, b, c} // This is the vector that contains the unique items.
{3, 1, 3} //a exists three times, b only one time, and c is three times.
To solve this I use the following:
1- Copy all the items in the vector of vector to single vector, so the output will be:
vec_vec{{a,a,b,c},{a,c,c}} -> vec{a,a,b,c,a,c,c}
2- Now I'm dealing with a single vector (not vector of vector), so it's much easier to sort, get the unique items and them (I may use the code here1 and here2)
Is converting the vector of vector to one vector is a good idea? Any better solution?
Can we find better way with less complexity comparing with the current way (c++11, c++14)?
From the top of my mind:
std::unordered_map<std::string, std::size_t> counters;
for(auto const& inner : vec_vec)
for(auto const& v : inner)
counters[v]++;
for(auto const& cnt : counters)
std::cout << cnt.first << " appears " << cnt.second << std::endl;
Use hash maps.
std::unordered_map<string, int> result;
for (const auto& x : vec_vec)
for (const string& y : x)
result[y]++;
I would just use a map as "tally" structure:
std::map<string, unsigned int> tally;
for(auto subvector : vector) { // subvector is std::vector<std::string>
for(auto item : subvector) { // item is a std::string
++tally[item];
}
}
If you insist on having the result as two parallel vectors (but why would you?) simply construct them from the map:
std::vector<std::string> unique_items;
unique_items.reserve(tally.size());
std::vector<unsigned int> counts;
counts.reserve(tally.size());
for(auto item : tally) {
unique_items.push_back(item.first);
counts.push_back(item.second);
}
If you don't want the result vector to be sorted, you can use an unordered_map, as suggested in other answers.