I have an STL map definition as follows:
map<string, map<int, string> > info;
I iterate that map using the following code:
for( map<string, map<int, string> >::iterator ii=info.begin(); ii!=info.end(); ++ii){
for(map<int, string>::iterator j=ii->second.begin(); j!=ii->second.end();++j){
cout << (*ii).first << " : " << (*j).first << " : "<< (*j).second << endl;
}
}
Is this the correct way to iterate or is there a better way to do so? The above code works for me, but I'm looking for a more elegant solution.
This is correct, it just lacks a few typedef and readability improvements :
typedef std::map<int, std::string> inner_map;
typedef std::map<std::string, inner_map> outer_map;
for (outer_map::iterator i = outerMap.begin(), iend = outerMap.end(); i != iend; ++i)
{
inner_map &innerMap = i->second;
for (inner_map::iterator j = innerMap.begin(), jend = innerMap.end(); j != jend; ++j)
{
/* ... */
}
}
If C++11 is available you may use range for loop:
for(auto &i: info) {
for(auto &j: i.second) {
/* */
}
}
If only C++11 auto is available:
for( auto i=info.begin(); i!=info.end(); ++i) {
for( auto j=i->second.begin(); j!=i->second.end(); ++j) {
/* */
}
}
If you may use BOOST there is BOOST_FOREACH:
typedef std::map<int, std::string> inner_map;
typedef std::map<std::string, inner_map> outer_map;
outer_map outer;
BOOST_FOREACH(outer_map::value_type &outer_value, outer){
BOOST_FOREACH(inner_map::value_type &inner_value, outer_value->second){
/* use outer_value and inner_value as std::pair */
}
}
While it's not clear what problem you are solving by having a map inside a map, I don't think there is a better way of iterating on all the items without using these iterators. The only thing you can do to improve code readability is to use typedefs on the template types.
However, won't it be a better idea to define your map as
multimap <string, MyClass>
where MyClass is defined as a pair of integer and a string, as well as a toString() method to dump the contents, etc?
If c++11 is available, we could use stl algorithm for_each and lambda functions to get a elegant solution
typedef map<int, string> INNERMAP;
typedef map<string, INNERMAP> OUTERMAP;
OUTERMAP theMapObject;
// populate the map object
// iterate the map object now
std::for_each(theMapObject.cbegin(), theMapObject.cend(),
[](const OUTERMAP::value_type& outerMapElement)
{
// process the outer map object
const INNERMAP& innerMapObject = outerMapElement.second;
std::for_each(innerMapObject.cbegin(), innerMapObject.cend(),
[](const INNERMAP::value_type& innermapElemen)
{
//process the inner map element
});
});
If you want to iterate through both maps, then the way you presented is the best way. Now, if there is something specific you want to do then you might be better with using a function from the algorithm header.
If you have access to C++11 features, then range-based for loops as proposed in Juraj Blaho's answer seem to be the most readable option to me. However, if C++17 is available to you, then you can use structured bindings together with those loops to further increase readability, as you can get rid of all first and second members:
std::map<std::string, std::map<int, std::string>> info;
for (const auto &[k1, v1] : info) {
for (const auto &[k2, v2] : v1) {
std::cout << k1 << " : " << k2 << " : " << v2 << std::endl;
}
}
Code on Coliru
Related
I have four static vectors. In my .cpp file (not my .h file!) I define these vector as such:
std::vector<Object*> ClassA::vecA;
std::vector<Object*> ClassA::vecB;
std::vector<Object*> ClassA::vecC;
std::vector<Object*> ClassA::vecD;
Then I populate each of these vectors with a number of objects of type Object.
Next I create a map:
std::map<std::string, std::vector<Object*> > cntr;
I populate this map with the vectors from above and a string as a Key for each vector.
The question is, how do I access the vectors in the map to find out the number of elements they contain. I have tried:
for (it = Cntr.begin(); it != Cntr.end(); it++)
{
if (it->first != token)
{
std::cout << it->first << std::endl;
int i = (it->second).size();
std::cout << "SIZE: " << i << std::endl;
}
}
However i always gives me the value of 1. What is the correct approach?
First off you need to set the iterator to point to an valid element of the map. When you do
std::map<std::string, std::vector<Object*>>::iterator Class::it;
int size = it->second.size();
it doesn't point to anything so using it is undefined behavior. What you can do though is use
std::map<std::string, std::vector<Object*>>::iterator Class::it;
it = cntr.begin();
int size = it->second.size();
Which now gives you the size of the first vector in the map.
If you want to get all of the sizes then you will need to iterate through the map. You can do this with a nice ranged based for loop like
for (const auto & elem : cntr) // get a const reference to each pair
std::cout << elem.second.size();
NathanOliver's answer should work if you have C++11. If you don't, you can try this, with a typedef to make the code clear:
typedef std::vector<Object*> MypObjVec;
typedef std::map<std::string, MypObjVec> MyMap;
MyMap::iterator Class::it = cntr.begin();
const MyMap::iterator Class::it_end = cntr.end();
for(; it!=it_end ; ++it)
{
std::cout<< it->second.size() << std::endl;
}
I'm trying to print out the five most used values. But when I changed my map to a multimap I broke the code where I add values to the map. How can I add values to a multimap? Can it be done in a similar way as I add values to a map?
// Create a map for keeping track of how many occurences of the different colors
multimap<string, int> hexmap;
// Add the hex values to the map
for(int i = 0; i < imagesize; i ++)
{
hexmap[colors[i]]++;
}
typedef std::multimap<int, string> Mymap;
Mymap dst;
std::transform(hexmap.begin(), hexmap.end(),
std::inserter(dst, dst.begin()),
[](const std::pair<string,int> &p )
{
return std::pair<int, string>(p.second, p.first);
}
);
Mymap::iterator st = dst.begin(),it;
size_t count = 5;
for(it = st; ( it != dst.end() ) && ( --count ); ++it)
std::cout << it->second << it->first << endl;
You add elements to a std::multimap<K, V> using insert() or emplace(), e.g.:
std::multimap<std::string, int> map;
map.insert(std::make_pair("hello" , 1));
map.insert({ "world", 0 });
map.emplace("hello", 0);
You'd locate objects in the std::multimap<K, V> using it's find() member, e.g.:
std::multimap<std::string, int>::iterator it = map.find("hello");
"I'm trying to print out the five most used values."
In this case, you don't have to use hexmap as std::multimap just std::map will do the job
However std::multimap for dst should be required
How can i get the top n keys of std::map based on their values?
Is there a way that i can get a list of say for example the top 10 keys with the biggest value as their values?
Suppose we have a map similar to this :
mymap["key1"]= 10;
mymap["key2"]= 3;
mymap["key3"]= 230;
mymap["key4"]= 15;
mymap["key5"]= 1;
mymap["key6"]= 66;
mymap["key7"]= 10;
And i only want to have a list of top 10 keys which has a bigger value compared to the other.
for example the top 4 for our mymap is
key3
key6
key4
key1
key10
note:
the values are not unique, actually they are the number of occurrences of each key. and i want to get a list of most occurred keys
note 2:
if map is not a good candidate and you want to suggest anything, please do it according to the c++11 ,i cant use boost at the time.
note3:
in case of using std::unordered_multimap<int,wstring> do i have any other choices?
The order of a map is based on its key and not its values and cannot be reordered so it is necessary to iterate over the map and maintain a list of the top ten encountered or as commented by Potatoswatter use partial_sort_copy() to extract the top N values for you:
std::vector<std::pair<std::string, int>> top_four(4);
std::partial_sort_copy(mymap.begin(),
mymap.end(),
top_four.begin(),
top_four.end(),
[](std::pair<const std::string, int> const& l,
std::pair<const std::string, int> const& r)
{
return l.second > r.second;
});
See online demo.
Choosing a different type of container may be more appropriate, boost::multi_index would be worth investigating, which:
... enables the construction of containers maintaining one or more indices with different sorting and access semantics.
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
int main(int argc, const char * argv[])
{
map<string, int> entries;
// insert some random entries
for(int i = 0; i < 100; ++i)
{
string name(5, 'A' + (char)(rand() % (int)('Z' - 'A') ));
int number = rand() % 100;
entries.insert(pair<string, int>(name, number));
}
// create container for top 10
vector<pair<string, int>> sorted(10);
// sort and copy with reversed compare function using second value of std::pair
partial_sort_copy(entries.begin(), entries.end(),
sorted.begin(), sorted.end(),
[](const pair<string, int> &a, const pair<string, int> &b)
{
return !(a.second < b.second);
});
cout << endl << "all elements" << endl;
for(pair<string, int> p : entries)
{
cout << p.first << " " << p.second << endl;
}
cout << endl << "top 10" << endl;
for(pair<string, int> p : sorted)
{
cout << p.first << " " << p.second << endl;
}
return 0;
}
Not only does std::map not sort by mapped-to value (such values need not have any defined sorting order), it doesn't allow rearrangement of its elements, so doing ++ map[ "key1" ]; on a hypothetical structure mapping the values back to the keys would invalidate the backward mapping.
Your best bet is to put the key-value pairs into another structure, and sort that by value at the time you need the backward mapping. If you need the backward mapping at all times, you would have to remove, modify, and re-add each time the value is changed.
The most efficient way to sort the existing map into a new structure is std::partial_sort_copy, as (just now) illustrated by Al Bundy.
since the mapped values are not indexed, you would have to read everything and select the 10 biggest values.
std::vector<mapped_type> v;
v.reserve(mymap.size());
for(const auto& Pair : mymap)
v.push_back( Pair.second );
std::sort(v.begin(), v.end(), std::greater<mapped_type>());
for(std::size_t i = 0, n = std::min<int>(10,v.size()); i < n; ++i)
std::cout << v[i] << ' ';
another way, is to use two maps or a bimap, thus mapped values would be ordered.
The algorithm you're looking for is nth_element, which partially sorts a range so that the nth element is where it would be in a fully sorted range. For example, if you wanted the top three items in descending order, you'd write (in pseudo C++)
nth_element(begin, begin + 3, end, predicate)
The problem is nth_element doesn't work with std::map. I would therefore suggest you change your data structure to a vector of pairs (and depending on the amount of data you're dealing with, you may find this to be a quicker data structure anyway). So, in the case of your example, I'd write it like this:
typedef vector<pair<string, int>> MyVector;
typedef MyVector::value_type ValueType;
MyVector v;
// You should use an initialization list here if your
// compiler supports it (mine doesn't...)
v.emplace_back(ValueType("key1", 10));
v.emplace_back(ValueType("key2", 3));
v.emplace_back(ValueType("key3", 230));
v.emplace_back(ValueType("key4", 15));
v.emplace_back(ValueType("key5", 1));
v.emplace_back(ValueType("key6", 66));
v.emplace_back(ValueType("key7", 10));
nth_element(v.begin(), v.begin() + 3, v.end(),
[](ValueType const& x, ValueType const& y) -> bool
{
// sort descending by value
return y.second < x.second;
});
// print out the top three elements
for (size_t i = 0; i < 3; ++i)
cout << v[i].first << ": " << v[i].second << endl;
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <algorithm>
#include <cassert>
#include <iterator>
using namespace std;
class MyMap
{
public:
MyMap(){};
void addValue(string key, int value)
{
_map[key] = value;
_vec.push_back(make_pair(key, value));
sort(_vec.begin(), _vec.end(), Cmp());
}
vector<pair<string, int> > getTop(int n)
{
int len = min((unsigned int)n, _vec.size());
vector<Pair> res;
copy(_vec.begin(), _vec.begin() + len, back_inserter(res));
return res;
}
private:
typedef map<string, int> StrIntMap;
typedef vector<pair<string, int> > PairVector;
typedef pair<string, int> Pair;
StrIntMap _map;
PairVector _vec;
struct Cmp:
public binary_function<const Pair&, const Pair&, bool>
{
bool operator()(const Pair& left, const Pair& right)
{
return right.second < left.second;
}
};
};
int main()
{
MyMap mymap;
mymap.addValue("key1", 10);
mymap.addValue("key2", 3);
mymap.addValue("key3", 230);
mymap.addValue("key4", 15);
mymap.addValue("key6", 66);
mymap.addValue("key7", 10);
auto res = mymap.getTop(3);
for_each(res.begin(), res.end(), [](const pair<string, int> value)
{cout<<value.first<<" "<<value.second<<endl;});
}
The simplest solution would be to use std::transform to build
a second map:
typedef std::map<int, std::string> SortedByValue;
SortedByValue map2;
std::transform(
mymap.begin(), mymap.end(),
std::inserter( map2, map2.end() ),
[]( std::pair<std::string, int> const& original ) {
return std::pair<int, std::string>( original.second, original.first );
} );
Then pick off the last n elements of map2.
Alternatively (and probably more efficient), you could use an
std::vector<std::pair<int, std::string>> and sort it
afterwards:
std::vector<std::pair<int, std::string>> map2( mymap.size() );
std::transform(
mymap.begin(), mymap.end()
map2.begin(),
[]( std::pair<std::string, int> const& original ) {
return std::pair<int, std::string>( original.second, original.first );
} );
std::sort( map2.begin(), map2.end() );
(Note that these solutions optimize for time, at the cost of
more memory.)
I have map as below
string word;
int occurance;
std::map< std::string, std::map<string, int> > map;
map[word]["count"] = occurance;
map output using iterator.
for(auto outer_iter=map.begin(); outer_iter!=map.end(); ++outer_iter) {
for(auto inner_iter=outer_iter->second.begin(); inner_iter!=outer_iter->second.end(); ++inner_iter) {
std::cout << outer_iter->first << '\t' << inner_iter->second << std::endl;
}
}
I want to display map by ordering the inner_iter->second value.
How can I do it?
The simplest approach to use (unless profiling indicates otherwise) is to simply make an inside-out map when you need to print:
std::multimap<int, std::string> inverse_map;
for(auto outer_iter=map.begin(); outer_iter!=map.end(); ++outer_iter)
{
for(auto inner_iter=outer_iter->second.begin(); inner_iter!=outer_iter->second.end(); ++inner_iter)
{
inverse_map.insert(std::make_pair(inner_iter->second, outer_iter->first));
}
}
Then you just loop through the inverted map and print normally.
EDIT: I think you can get the double sorting you want by instead using a set of pairs:
std::set<std::pair<int, std::string> > inverse_map;
for(auto outer_iter=map.begin(); outer_iter!=map.end(); ++outer_iter)
{
for(auto inner_iter=outer_iter->second.begin(); inner_iter!=outer_iter->second.end(); ++inner_iter)
{
inverse_map.insert(std::make_pair(inner_iter->second, outer_iter->first));
}
}
You can insert into a vector and sort the contents accordingly.
Edit: Modified to sort from highest to lowest.
typedef std::pair<std::string, int> hist_item;
std::vector<hist_item> hist;
hist.reserve(map.size());
for(auto outer_iter=map.begin(); outer_iter!=map.end(); ++outer_iter) {
for(auto inner_iter=outer_iter->second.begin();
inner_iter!=outer_iter->second.end(); ++inner_iter) {
hist.push_back(std::make_pair(outer_iter->first, inner_iter->second));
}
}
std::sort(hist.begin(), hist.end(),
[](const hist_item &a,const hist_item &b){return a.second>b.second;});
for (auto i = hist.begin(); i!= hist.end(); ++i) {
std::cout << i->first << '\t' << i->second << std::endl;
}
This mimics your original output. I am not sure of the purpose of the inner map. If you are tracking properties other than "count" in it, your original output and this routine loses that association, and you just get multiple numbers associated with the outer word.
If you're open to using Boost, you can use Boost::Bimap, which lets you associate words with counts, and counts with words (at the same time). This example shows how to compute the word count of a text and displays the histogram.
If you only need to display the sorted word count occasionally, it might be faster to use a regular std::map for building the word count map. Then you generate the sorted word count as needed using the techniques shown in the other answers. You'll probably have to run benchmarks to know which is faster.
For the sake of completeness, I'll add another solution that uses a heapsort by pushing the maps pairs through a std::priority_queue to obtain the word count sorted by occurrence:
#include <iostream>
#include <map>
#include <queue>
typedef std::map<std::string, int> MyMap;
struct OrderByOccurence
{
bool operator()(MyMap::const_reference lhs, MyMap::const_reference rhs)
{
// This will make the priority queue sort from highest word count
// to lowest.
return lhs.second < rhs.second;
// This will make the priority queue sort from lowest word count
// to highest.
// return rhs.second < lhs.second;
// Here, you can also check that if both counts are the same,
// the elements should be ordered alphabetically by word.
}
};
int main()
{
MyMap m = {{"a", 1}, {"b", 2}, {"c", 3}};
std::priority_queue<std::pair<std::string, int>,
std::vector< std::pair<std::string, int> >,
OrderByOccurence> q;
for (auto it=m.begin(); it!=m.end(); ++it)
q.push(*it);
while (!q.empty())
{
std::cout << q.top().first << " " << q.top().second << "\n";
q.pop();
}
}
I have a map and I want the first column i.e (*it).first to be pushed back into a vector then (*it)->second to be pushed back into another vector
Is this the best way to do it?
std::vector<std::string>test;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
}
My other question is if i have a loop i.e
how would I insert all the integers i into (*it).first?
for(int i = 0; i < 10; i++)
{
// 1 - 10 will go in (*it).first
}
I want to have some integers in (*it).first and have associated values in (*it).second;
Use std::transform.
First define two functions key and value which take the pair of strings and return the first or second value, respectively.
#include <map>
#include <vector>
#include <algorithm>
#include <iostream>
#include <iterator>
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;
}
Then use std::transform from <algorithm> with the functions to transform the map into either a vector of keys or a vector of values.
int main()
{
using namespace std; // be explicit normally, trying to be brief here
map<string, string> contacts;
contacts["alice"] = "555-2701";
contacts["bob"] = "555-2702";
vector<string> keys(contacts.size());
vector<string> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
cout << "Keys:\n";
copy(keys.begin(), keys.end(), ostream_iterator<string>(cout, "\n"));
cout << "\n";
cout << "Values:\n";
copy(values.begin(), values.end(), ostream_iterator<string>(cout, "\n"));
return 0;
}
Output:
Keys:
alice
bob
Values:
555-2701
555-2702
Your first question, "how can I push the first column of my map into one vector and the 2nd column into another" is solved thus:
std::map<std::string, std::string> mymap;
std::vector<std::string> keys;
std::vector<std::string> values;
for ( std::map<std::string,std::string>::iterator it=mymap.begin() ; it != mymap.end(); ++it )
{
keys.push_back(it->first);
values.push_back(it->second);
}
Your second question, "how would insert all the integers i into (*it).first ?" is solved thus:
std::map<int, int> mymap2;
for(int i = 0; i < 10; i++)
{
// Insert default value into map
// This sets '(*it).first' to 'i' and
// '(*it).second' to a default value (in
// this case, 0).
mymap2[i];
}
or
std::map<int, int> mymap3;
for(int i = 0; i < 10; i++)
{
// Insert specified value into map
// this sets '(*it).first' to 'i', and
// '(*it).second' to the value returned from the function.
maymap3[i] = ChooseSpecificValue(i);
}
Well, it can be done with a simple loop:
for (auto const& p: mymap) {
vec1.push_back(p.first);
vec2.push_back(p.second);
}
Or using the std::transform algorithm, though it's quite verbose here:
std::transform(mymap.begin(), mymap.end(), std::back_inserter(vec1),
[](MyMap::const_reference p) { return p.first; });
Assuming you've declared your map as string key and value (ie map<string, string> mymap; then it would be like below, also assuming you've declare 'it' variable as map<string, string>::iterator it, etc:
std::vector<std::string> test;
std::vector<std::string> second;
std::map<string, string>::iterator it;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
second.push_back((*it).second);
}
Not sure about your next question.
The first part of your question:
std::vector<std::string> test;
std::vector<std::string> test2; // assuming map is from string to string
for (it = mymap.begin(); it != mymap.end(); ++it)
{
test.push_back(it->first); // push first in one vector
test2.push_back(it->second); // push second in another vector
}
So, yes a simple for can do what you want.
The second part of your question:
Since you are updating the key of the map, you would need to remove it from the map and insert the changed one. So:
std::string first, second;
first = it->first;
second = it->second;
mymap.erase(it); // be careful with invalidating iterator
// change first
mymap[first] = second;
To change first by adding all integers i to it, that would really depend on the type of first. For example with a string, you may mean something like this:
ostringstream sout;
for (int i = 0; i < 10; ++i)
sout << (i?" ":"") << i;
first = sout.str();
Or if first is for example a set, you may mean something like this:
for (int i = 0; i < 10; ++i)
first.insert(i);
and my other question is if i have a loop i.e how would insert all the
integers i into (*it).first?
In the case of a std::map, you can't modify the iterator returned like that ... the key member (i.e., the first) in the std::map key/value pair data-structure is intentionally designated as a constant value, and is initialized to its constant value at the beginning of the key/value pair's lifetime in the std::map data-structure. If the keys weren't constant, you would end up creating havoc when you change the key, since the nodes in a std::map are suppose to be sorted by the keys. The second member of the key/value pair data-structure is the member that can be changed.
So if you want to insert a set of key/value pairs in a map, you could simply do the following:
std::map<int, int> mymap;
int some_other_value = 100;
for (int i=0; i < 10; i++)
{
mymap[i] = some_other_value++;
}
it here will be an iterator which will point to one of the position in map and at max have one first and second value for one iterator . At max you can have multiple key or same key holding same/different values depending on key/value combination.
As far as pushing the value in the vector for a key in map is concern you can do it in the same way you are pushing the key
std::vector<std::string>test;
std::vector<std::string>test2;
for ( it=mymap.begin() ; it != mymap.end(); it++ )
{
test.push_back((*it).first);
test2.push_back((*it).second);
}
Neways yours question is very unclear .
Just in case you want to deal with different data types in your map I would template a generic copy function:
template <class A, class B>
void mycopy(std::map<A, B>&m, std::list<A>& keys, std::list<B>& values) {
typename std::map<A, B>::iterator it;
for (it = m.begin(); it != m.end(); ++it) {
keys.push_back( (*it).first );
values.push_back( (*it).second );
}
}
Mixing it up:
std::map<int, std::string> mymap;
std::list<int> keys;
std::list<std::string> values;
mymap[1] = "string1";
mymap[2] = "string2";
mycopy(mymap, keys, values);
std::map<std::string, int> mymap1;
std::list<std::string> keys1;
std::list<int> values1;
mymap1["string1"] = 1;
mymap1["string2"] = 2;
mycopy(mymap1, keys1, values1);
Edit: yes __copy isnt the best definition. Thanks