Extract second element from a three element map in c++ - 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;.

Related

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.

Mapping one vector to different pairs even though the vector is different by the time it maps to each pair?

I am having some trouble mapping vectors to integer pairs. My question comes down to, If a have only one vector that am clearing at the beginning of a for loop, filling and then assigning to a pair, will I run into errors because I am assigning the same vector to different integer pairs, even though the vector is different each time when I assign the values?
Essentially, if I have:
map[Pair1]=Vector1;
Vector1.push_back(1);
Map[Pair2]=Vector1;
Will Pair1 and Pair2 be assigned the same vector of values?
It depends on the map itself.
If it is a map of std::vector values or objects (as in std::map<std::pair<int, int>, std::vector<int>>) then each vector in the map will be distinct and unique. Each key will have its own vector object.
With your example, map[Pair1] will be a different vector from map[Pair2]. No modifications to Vector1 will propagate to map[Pair1] automatically.
Will Pair1 and Pair2 be assigned the same vector of values?
No.
Firstly, keep in mind that STL container value types need to be assignable so you can not use references as value in the map because they are not assignable. On the another hand, you can use pointers but this will result to be same value for all of the keys in the map if you implement the code like you mentioned.
So, as long as you use assignable types (something like following), you will be good.
std::map<pair<int,int> , vector<int> > myMap;
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
myMap[std::make_pair(1,1)] = vec;
for(int i=0; i< myMap[make_pair(1,1)].size(); ++i)
cout << myMap[make_pair(1,1)][i] << " - " ;
cout << endl;
vec.clear();
vec.push_back(4);
vec.push_back(5);
myMap[make_pair(2,1)] = vec;
for(int i=0; i< myMap[make_pair(2,1)].size(); ++i)
cout << myMap[make_pair(2,1)][i] << " - " ;

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

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();

C++ Vector of linked lists

I have a nested template argument in the form of
vector<list<int, string> >.
That is, it's a vector of linked lists that hold integer values and string words. If this is not a valid form, please let me know. My question is in calling it. If 'table' is of the above data type, would it be okay to call an index as, for example, table[0]? If so, how do I start walking through the linked list in that index?
With the nested data structure you've defined, you are exactly correct to call an index like you mentioned, table[0]. You can perform your list operations on that exactly. To make your code more clean, it can be helpful to do something like:
list<string> listInVector = table[i];
So you don't get confused performing operations on an index in table, instead you can use that identifier to make the code more clean.
The elements in the containers are individual elements. If you want to have multiple elements, e.g., an int and a std::string you'll need to put them into a suitable structure, e.g., into a std::pair<int, std::string>:
std::vector<std::list<std::pair<int, std::string>>> table;
To walk the elements in the list at a specific position you could use, e.g.:
std::list<std::pair<int, std::strin>>::iterator it(table[i].begin()), end(table[i].end());
for (; it != end; ++it) {
std::cout << "int=" << it->first << " string=" << it->second << "\n";
}
"If this is not a valid form, please let me know"
Yes its not valid, I think you meant :-
std::vector< std::list<std::pair<std::string, int> > > table ;
How to access ?
Something like :-
typedef std::pair<std::string , int> ele;
std::list<ele>::iterator it = table[i].begin(); //for ith table
for(;it!=table[i].end();++it)
std::cout<<it->first<<" "<<it->second;

c++ container search by key and value

I'm trying to build a container of string representations of ordinal numbers, searchable both by the string and by the number. For example, I can do it trivially, but inefficiently, with an array:
std::string ordinalStrings = {"zeroth", "first", "second",..}
getting the string with ordinalStrings[number], and the integer with a while(not found) loop, but I figure that one of the STL containers would do a better job, just not sure which one.
It needn't be searchable by both, for example, simply getting the numerical key of a map with a string key would work, but there doesn't seem to be a function for it, and besides, it seems a little messy.
As an aside, is it possible to have a constant STL container? I only need the searching functionality, not the insertion. Also, since I assume I can't, is it possible to fill the container without an initializer function? That is, can a header simply say:
std::map<std::string, int> ordinals;
ordinals["zero"] = 0;
ordinals["one"] = 1;
...
It just seems silly to have an initializer function for what is essentially a constant value;
Thanks,
Wyatt
Stephen suggested using Boost.MultiIndex, however it's a bit an overkill.
Boost.Bimap has been developped over it, and offer extra functionalities. It is in fact tailored right for this task. It's also one of the few libraries which has (imho) a good documentation :)
Here is an example right from this page.
#include <iostream>
#include <boost/bimap.hpp>
struct country {};
struct place {};
int main()
{
using namespace boost::bimaps;
// Soccer World cup.
typedef bimap
<
tagged< std::string, country >,
tagged< int , place >
> results_bimap;
typedef results_bimap::value_type position;
results_bimap results;
results.insert( position("Argentina" ,1) );
results.insert( position("Spain" ,2) );
results.insert( position("Germany" ,3) );
results.insert( position("France" ,4) );
std::cout << "Countries names ordered by their final position:"
<< std::endl;
for( results_bimap::map_by<place>::const_iterator
i = results.by<place>().begin(),
iend = results.by<place>().end() ;
i != iend; ++i )
{
std::cout << i->get<place >() << ") "
<< i->get<country>() << std::endl;
}
std::cout << std::endl
<< "Countries names ordered alphabetically along with"
"their final position:"
<< std::endl;
for( results_bimap::map_by<country>::const_iterator
i = results.by<country>().begin(),
iend = results.by<country>().end() ;
i != iend; ++i )
{
std::cout << i->get<country>() << " ends "
<< i->get<place >() << "º"
<< std::endl;
}
return 0;
}
You can provide two different lookup mechanisms using boost::multi_index (here's an example of a bidirectional map using multi_index). Another (maybe simpler) option is to maintain two containers: one to lookup by ordinal, one to search by string. You could use two std::map or a std::map and a std::vector (for constant time lookup of ordinality).
As an aside, is it possible to have a constant STL container? I only need the searching functionality, not the insertion.
Yes, but you must initialize a non-const container. You can then copy that into a const container. For example, you can use a helper function: const std::map<string, int> ordinals = create_map();
That is, can a header simply say: ...
No, you should initialize the container within a proper control flow.