How can I print a map of the following type:
map<string, vector<string>>?
Everything I have tried so far has resulted in an error.
It's very easy, just implement :
std::ostream & operator<<(std::ostream & os, const map<string, vector<string>>& myMap)
One simple implementation might be.
std::ostream & operator<<(std::ostream & os, const map<string, vector<string>>& myMap)
{
for(auto elem : myMap) {
std::os << it->first << " -> ";
auto & vec = it->second;
for(int i=0;i<vec.size();i++){
os << vec[i] << " ";
}
os << "\n";
}
os<<"\n";
return os;
}
You could try something like this (tested in Visual Studio 2013).
Strictly speaking, you should include the code you tried yourself to solve the problem. You said everything you tried gave an error. Just post whatever you tried and the exact error you got.
#include "stdafx.h"
#include <vector>
#include <map>
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char* argv[])
{
map<string, vector<string>> myMap;
myMap["key1"] = { "m00", "m01", "m02" };
myMap["key2"] = { "m10", "m11" };
myMap["key3"] = { "m20", "m21", "m22", "m23" };
for (auto m : myMap)
{
string const & key = m.first;
vector<string>& v = m.second;
cout << "Key=" << key.c_str();
for (auto i : v)
cout << " " << i.c_str();
cout << endl;
}
return 0;
}
Related
I have a class Twhich has a map of string class T
For convinience, I would like to print the content in the class in an organized manner with tabulations.
As an example, I have:
#include <iostream>
#include <map>
#include <string>
class test;
typedef std::map<std::string, std::string> sMap;
typedef std::map<std::string, test> testMap;
class test {
public:
sMap map1;
testMap map2;
};
std::ostream& operator<<(std::ostream& os, const sMap& smap) {
for(const auto& s_smap : smap) {
os << s_smap.first
<< "\t"
<< s_smap.second
<< "\n";
}
return os;
}
std::ostream& operator<<(std::ostream& os, const test& t) {
os << t.map1
<< "\n";
for (const auto& s_map : t.map2) {
os << s_map.first
<< "\t"
<< s_map.second
<< std::endl;
}
return os;
}
int main() {
sMap myMap;
myMap["a"] = "b";
test obj;
obj.map1 = myMap;
test obj2;
obj2.map2.insert({"one", obj});
obj2.map2["one"].map2.insert({"two", obj});
obj2.map2["one"].map2["two"].map2.insert({"three", obj});
obj2.map2["one"].map2["two"].map2["three"].map2.insert({"four", obj});
std::cout << obj2 << std::endl;
return 0;
}
I would like the output to be:
one a b
two a b
three a b
four a b
How can the operator<< be overloaded to achieve this?
Doing this means you need to pass along extra information. You could create a printMap kind of fucntion that takes an indentation level as an argument, but you've said you want to achieve this by overloading operator<< so you can't pass that along unless you wrap your test objects in another class/struct that does carry that info.
That might look something like the below, which outputs:
% ./a.out
a b
a b
a b
a b
Tweaking this is left as an exercise for the OP.
#include <iostream>
#include <map>
#include <string>
struct test;
using sMap = std::map<std::string, std::string>;
using testMap = std::map<std::string, test>;
struct test {
sMap map1;
testMap map2;
};
struct indent {
const test &ref;
int indentLevel;
indent(const test &ref, int indentLevel=0)
: ref(ref), indentLevel(indentLevel)
{ }
};
std::ostream& operator<<(std::ostream& os, const indent &i) {
int j = i.indentLevel;
for (const auto &[f, s] : i.ref.map1) {
for (int k = 0; k < j; k++) os << "\t";
os << f << "\t" << s << "\n";
}
for (const auto &[f, s] : i.ref.map2) {
os << indent(s, j + 1);
}
return os;
}
std::ostream& operator<<(std::ostream& os, const sMap& smap) {
for (const auto &[f, s] : smap) {
os << f << "\t" << s << "\n";
}
return os;
}
std::ostream& operator<<(std::ostream& os, const test& t) {
os << t.map1
<< "\n";
for (const auto& s_map : t.map2) {
os << s_map.first
<< "\t"
<< s_map.second
<< std::endl;
}
return os;
}
int main() {
sMap myMap;
myMap["a"] = "b";
test obj;
obj.map1 = myMap;
test obj2;
obj2.map2.insert({"one", obj});
obj2.map2["one"].map2.insert({"two", obj});
obj2.map2["one"].map2["two"].map2.insert({"three", obj});
obj2.map2["one"].map2["two"].map2["three"].map2.insert({"four", obj});
std::cout << indent(obj2) << std::endl;
return 0;
}
Some aspects of this case incorporate post-C++11 features like structured bindings. If a newer standard cannot be used, these techniques can be converted to C++11 friendly constructs.
In the following code, [id, name] is a const reference. However, studentMap is non-const. The user can change the value of studentMap in the loop.
I want to ask whether there is a way to make the StudentMap also const. Thanks.
#include <iostream>
#include <string>
#include <map>
int main() {
std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
studentMap[7] = "Jack";
studentMap[15] = "John";
for (const auto& [id, name] : studentMap) {
studentMap.at(id) += "test";
}
for (const auto& [id, name]: studentMap) {
std::cout << id << " " << name << "\n";
}
return 0;
}
No I don't think it is possible to change the type of a variable.
If you want to avoid unexpected mistake of modifying studentMap, you could pull the logic into a separate function, and refer studentMap with a const ref:
#include <iostream>
#include <string>
#include <map>
void displayStudentMap(const auto& studentMap) {
for (const auto& [id, name] : studentMap) {
// compilation error
studentMap.at(id) += "test";
}
for (const auto& [id, name]: studentMap) {
std::cout << id << " " << name << "\n";
}
}
int main() {
std::map<int, std::string> studentMap;
studentMap[1] = "Tom";
studentMap[7] = "Jack";
studentMap[15] = "John";
displayStudentMap(studentMap);
}
This way:
const std::map<int, std::string> studentMap {
std::make_pair(1, "Tom"),
std::make_pair(7, "Jack"),
std::make_pair(15, "John")
};
I have the program that has two vectors of names and ages. It sorts the names vector and keeps the age vector in the correct order to match the sorted name vector. Now, I want to make a function from existing code, but I have some issues.
Existing code:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iomanip>
using namespace std;
int main() {
vector<string> names {"One", "Two", "Three", "Four", "Five"};
vector<unsigned int> ages { 1, 2, 3, 4, 5};
const vector<string> namesCopy = names;
sort(begin(names), end(names));
decltype(ages) sortedAges(ages.size());
for(int i = 0; i < namesCopy.size(); ++i) {
const auto iter = lower_bound(begin(names), end(names), namesCopy[i]);
const auto pos = iter - begin(names);
sortedAges[pos] = ages[i];
}
for(int i = 0 ; i < names.size() ; ++i)
cout << setw(10) << names[i] << setw(4) << sortedAges[i] << '\n' ;
}
Output
Function:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iomanip>
using namespace std;
int test(vector<string> testNames, vector<string> testNamesCopy, vector<unsigned int> testAges, vector<unsigned int> testSortedAges) {
for(int i = 0; i < testNamesCopy.size(); ++i) {
const auto iter = lower_bound(begin(testNames), end(testNames), testNamesCopy[i]);
const auto pos = iter - begin(testNames);
return testSortedAges[pos] = testAges[i];
}
}
int main() {
vector<string> names {"One", "Two", "Three", "Four", "Five"};
vector<unsigned int> ages { 1, 2, 3, 4, 5};
const auto namesCopy = names;
sort(begin(names), end(names));
decltype(ages) sortedAges(ages.size());
for(int i = 0 ; i < names.size() ; ++i)
cout << setw(10) << names[i] << setw(4) << test(names, namesCopy, ages, sortedAges) << '\n' ;
}
Output 2
I think you are approaching this the wrong way. Having 2 vector that you sort but have to keep in the same order is error prone. Instead you should use a vector of pair.
std::vector<std::pair<std::string, int>> idendityVec;
Then you can sort by the name (the first element of the pair) by doing
std::sort(idendityVec.begin(), idendityVec.end());
If you want to sort by age, you can declare your own comparaison function and use it in the sort :
bool lesserAge(const pair<std::string,int> &a,
const pair<std::string,int> &b)
{
return (a.second < b.second);
}
std::sort(idendityVec.begin(), idendityVec.end(), lesserAge);
Which gives you something like this :
#include <iostream>
#include <vector>
#include <algorithm>
#include <utility>
bool lesserAge(const std::pair<std::string, int> &a,
const std::pair<std::string, int> &b)
{
return (a.second < b.second);
}
int main()
{
std::vector<std::pair<std::string, int>> idendityVec = {std::make_pair("three", 3), std::make_pair("four", 4), std::make_pair("two", 2), std::make_pair("one", 1)};
for (auto v : idendityVec)
{
std::cout << "Name=" << v.first << ", age=" << v.second << std::endl;
}
// Sort by age i.e. second element
std::sort(idendityVec.begin(), idendityVec.end(), lesserAge);
for (auto v : idendityVec)
{
std::cout << "Name=" << v.first << ", age=" << v.second << std::endl;
}
//Sort by name i.e first element
std::sort(idendityVec.begin(), idendityVec.end());
for (auto v : idendityVec)
{
std::cout << "Name=" << v.first << ", age=" << v.second << std::endl;
}
}
vector<string> names {"One", "Two", "Three", "Four", "Five"};
vector<unsigned int> ages { 1, 2, 3, 4, 5};
names and ages seem connected in such a way that it'd be best to group them together in a class. We can use a simple struct which, by default, gives you direct access to its members, just like you have access to all the names and ages in your current solution. You can start with this:
struct person { // ... or animal, or thing. Give it a meaningful name.
std::string name{};
unsigned age{};
};
Now you can create a std::vector<person> instead of having two unconnected vectors, which makes sorting and general handling of the data a bit of a hassle.
With the above, sorting and printing etc. becomes more straight forward. I've used lambdas to create the sorting functions in the example:
#include <algorithm>
#include <iostream>
#include <string>
#include <vector>
#include <tuple> // std::tie
#include <iomanip>
struct person {
std::string name{};
unsigned age{};
};
// print one "person"
std::ostream& operator<<(std::ostream& os, const person& p) {
return os << std::setw(10) << p.name << std::setw(4) << p.age;
}
int main() {
// one vector with all the persons
std::vector<person> persons{
{"One", 1},
{"Two", 2},
{"Three", 3},
{"Four", 4},
{"Five", 5}
};
// sort on name first, age second (if names are equal) - ascending order
std::sort(persons.begin(), persons.end(), [](const person& a, const person& b) {
return std::tie(a.name, a.age) < std::tie(b.name, b.age);
});
// print the current order:
for(const auto& p : persons) std::cout << p << "\n";
std::cout << "--\n";
// sort on age first, name second (if ages are equal) - ascending order
std::sort(persons.begin(), persons.end(), [](const person& a, const person& b) {
return std::tie(a.age, a.name) < std::tie(b.age, b.name);
});
// print the current order:
for(const auto& p : persons) std::cout << p << "\n";
}
std::map<std::string, std::vector<string>> data;
In order to print out this by using copy, how should my std::ostream_iterator be?
Apparently std::ostream_iterator<std::pair<std::string, std::vector<std::string>>> out_it(std::cout, "\n"); did not make it.
My operator<< overload is the following std::ostream& operator<<(std::ostream& out, const std::pair<std::string, std::vector<std::string>>& p) and it writes out the p.first and p.second and returns it.
If you do any serious programming in C++, you will eventually need a generic way to print out collections.
Here is the basis of one:
#include <iostream>
#include <map>
#include <vector>
#include <string>
// introduce the concept of an object that emits values to an ostream
// by default it simply calls operator <<
template<class T> struct emitter
{
using arg_type = T;
emitter(const T& v) : v_(v) {}
friend std::ostream& operator<<(std::ostream& os, const emitter& e) {
return os << e.v_;
}
const T& v_;
};
// introduce the concept of an io manipulator called emit
template<class T> auto emit(const T& v) -> emitter<T>
{
return emitter<std::decay_t<T>>(v);
}
// specialise the emitter for maps
template<class K, class V, class C, class A>
struct emitter<std::map<K, V, C, A>>
{
using arg_type = std::map<K, V, C, A>;
emitter(const arg_type& v) : v_(v) {}
friend std::ostream& operator<<(std::ostream& os, const emitter& e) {
const char* elem_sep = "\n\t";
const char* end_sep = " ";
os << "{";
for (const auto& elem : e.v_)
{
os << elem_sep << emit(elem.first) << ": " << emit(elem.second);
end_sep = "\n";
}
return os << end_sep << "}";
}
const arg_type& v_;
};
// specialise the emitter for vectors
template<class V, class A>
struct emitter<std::vector<V, A>>
{
using arg_type = std::vector<V, A>;
emitter(const arg_type& v) : v_(v) {}
friend std::ostream& operator<<(std::ostream& os, const emitter& e) {
const char* elem_sep = " ";
const char* end_sep = " ";
os << "[";
for (const auto& elem : e.v_)
{
os << elem_sep << emit(elem);
elem_sep = ", ";
}
return os << end_sep << "]";
}
const arg_type& v_;
};
int main() {
// build test data
std::map<std::string, std::vector<std::string>> data;
data.emplace("a", std::vector<std::string>{ "now", "is", "the", "time" });
data.emplace("b", std::vector<std::string>{ "for", "all", "good", "men" });
data.emplace("c", std::vector<std::string>{ "to", "come", "to", "the" });
data.emplace("d", std::vector<std::string>{ "aid", "of", "their", "party" });
// request an emitter manipulator
std::cout << emit(data) << std::endl;
}
Expected output:
{
a: [ now, is, the, time ]
b: [ for, all, good, men ]
c: [ to, come, to, the ]
d: [ aid, of, their, party ]
}
So here is a operator<< that will print out the contents of one pair from your map:
std::ostream& operator<<(std::ostream& out, const std::pair<std::string, std::vector<std::string>>& p) {
out << p.first << ": "; // prints the string from key
for (const auto& i : p.second) // loops throught the whole vector that is asociated with that key
out << i << ", ";
return out;
}
So to use it in this example. If you ennter this into your map:
std::map<std::string, std::vector<string>> data;
std::vector<std::string> vec = {"VAL1", "VAL2", "VAL3"};
data.insert(std::make_pair("KEY", vec));
auto it = data.find("KEY");
std::cout << *it;
This would be what wil get printed out using the operator<< above:
KEY: VAL1, VAL2, VAL3,
You can also change the formatting a bit so the comma isn't after the last value as well but that's only a cosmetic problem. Your problem was in that you wanted to print vector while it doesn't have std operator<<. So in order to print vector you must manually loop through it's content like in my example with the ranged for.
To custom print your vector, you'd have to write some code yourself. To make sure your custom operator is used, I suggest you use std::stringstream to turn your key-value pair into a string, which you'd then feed into an std::ostream_iterator<std::string>.
Something, like this (pardon the using namespace std;):
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <algorithm>
#include <sstream>
#include <map>
using namespace std;
int main() {
map<string, vector<string>> m {
{"a", {"1", "2"}},
{"b", {"1", "2"}},
{"b", {"1", "2"}},
};
transform(begin(m), end(m), ostream_iterator<string>(cout), [](auto& p){
stringstream ss;
ss << p.first << ", {";
bool first = true;
for (auto& s : p.second)
{
ss << (first ? "" : ", ") << s;
first = false;
}
ss << "}\n";
return ss.str();
});
return 0;
}
I didn't actually write the operator<<, but you can substitue yours to make the lambda body short);
I have a map like this:
map<string, map<int, int>> collector;
And I have no idea how to insert data in my map. If I had
map<string, int> collector;
with only key-value I would use
collector.insert(pair<string, int>)(name,money));
But what is the way of inserting when we have map in map. I tried to do:
typedef map<int, int> map_;
for(iteration = collector.begin(); iteration != collector.end(); iteration++) {
iteration = collector.find(word);
if(iteration == collector.end()) {
iteration = collector.insert(map_::value_type(num,num).first;
}
}
This way is not working for me.
Here are some ways to insert into your data structure:
#include <iostream> // cout
#include <map>
#include <string>
#include <utility> // make_pair
using namespace std;
int main()
{
using Collector = map<string, map<int, int>>;
Collector collector;
collector["USD"] = Collector::mapped_type{ { 1, 3 }, { 0, 8 } };
collector["EUR"].insert(make_pair(4, 5));
collector["EUR"].insert(make_pair(6, 7));
collector["CHF"][2] = 4;
const Collector::mapped_type jpyIntegers { { 10, 20 }, { 100, 200 } };
collector.insert(make_pair("JPY", jpyIntegers));
collector["MMK"];
for (const auto& a: collector) {
cout << a.first << ": ";
for (const auto& i: a.second) {
cout << "(" << i.first << "|" << i.second << ")";
}
cout << "\n";
}
}