Function that returns map in c++ - c++

Can someone please give an actual example of function that returns map in c++.
I tried answers from other posts but I don't know how to apply in my case.
This is my working code:
auto DataArray = jvalue.at(U("data")).as_array();
//Make an associative array or map with key value pair from extracted json data
std::map<int, std::string> staffMap;
// loop through 'data' object
for (int i = 0; i < DataArray.size(); i++)
{
try
{
auto data = DataArray[i];
auto dataObj = data.as_object();
int key;
std::string value;
// loop through each object of 'data'
for (auto iterInner = dataObj.cbegin(); iterInner != dataObj.cend(); ++iterInner)
{
auto &propertyName = iterInner->first;
auto &propertyValue = iterInner->second;
//std::wcout << "Property: " << propertyName << ", Value: " << propertyValue << std::endl;
if (propertyName == L"_id")
{
key = propertyValue.as_integer();
}
else if (propertyName == L"name")
{
value = conversions::to_utf8string(propertyValue.as_string());
}
}
staffMap.insert(std::make_pair(key, value));
}
catch (const std::exception& e)
{
std::wcout << e.what() << std::endl;
}
}
// Iterate through map and display in terminal
std::map<int, std::string>::iterator iter;
std::wcout << "The list of staffs" << std::endl;
for (iter = staffMap.begin(); iter != staffMap.end(); iter++)
std::cout << iter->first << " " << iter->second << " ,";
Let say I want a function:
std::map<int, std::string> staffMap;
std::map<> GetStaffMap()
{
return staffMap;
}
// Give staffMap a data here
I can't find enough tutorial for making a function that returns std::map in c++. Hope someone could help me here. Thank you.

I can't find enough tutorial for making a function that returns std::map in c++. Hope someone could help me here
You need to specify the exact type, std::map<int, std::string>:
std::map<int, std::string> GetStaffMap()
{
return staffMap;
}
If you are able to use C++14, use auto as an alternative:
auto GetStaffMap()
{
return staffMap;
}

The example below shows how to create a function in C++ that returns a map.
// Example program with a function returning a map
#include <iostream>
#include <string>
#include <map>
std::map<std::string, int>
function()
{
std::map<std::string, int> out;
out["one"] = 1;
out["two"] = 2;
return out;
}
int main()
{
std::map<std::string, int> out = function();
for (const auto & iter : out)
std::cout << iter.first << " = " << iter.second << std::endl;
}

Related

Access map element via pointer in C++

I switched from c to c++ recently and just can't figure out what I'm doing wrong here.
I would like to access and set the member of a map via another function.
Here is my example which you can just copy to cpp.sh or so if you like
#include <iostream>
#include <map>
using namespace std;
struct test{
int i;
int j;
};
void addValues(test* val){
if (val == NULL){
val = new test();
cout<<"new";
}
val->i = 10;
val->j = 12;
}
void printVal(test* val){
cout<<"finish " << val->i << " " << val->j;
}
int main()
{
map<string, test*> bla = {{"test1",NULL}};
addValues(bla.at("test1"));
printVal(bla.at("test1"));
return 0;
}
code from my project is a little bit more complex but it's basically this problem. I created a test in addValues() and have not deleted it. Why am I not able to print this value in printVal()? What am I missing?
Thanks in advance!
Parameters are passed by value. Pointers are no exception to that. Your addValues modifies a local copy of the pointer when a nullptr is passed. Modifying that local copy does not affect the pointer in the map. Pass the pointer by reference:
void addValues(test*& val){
if (val == nullptr){
val = new test();
cout<<"new";
}
val->i = 10;
val->j = 12;
}
Or better yet, do not use raw pointers in the first place. Moreover, consider to write a constructor that initializes the members of test instead of relying on the caller to initialize them.
Example :
#include <iostream>
#include <map>
//using namespace std; NO teach yourself not to do this.
struct test
{
int i = 0; // <== in c++ you can initialize values of structs
int j = 0;
};
// this instead of printVal
std::ostream& operator<<(std::ostream& os, const test& t)
{
os << "i = " << t.i << ", j = " << t.j << "\n";
return os;
}
int main()
{
std::map<std::string, test> map =
{
{"test1",{1,1}},
{"test2",{2,2}},
};
// loop over all entries in the map
// range based for loop.
// each entry in the map is a key,value pair (not they key, not the value but a pair)
// https://en.cppreference.com/w/cpp/language/range-for
std::cout << "range based for over keyvalue pairs\n";
for (const auto& kv : map)
{
// note kv.second is where we use operator<< from earlier.
std::cout << "Key : " << kv.first << ", value : " << kv.second << "\n";
}
std::cout << "\n";
// structured bindings make code more readable
// https://en.cppreference.com/w/cpp/language/structured_binding
std::cout << "range based for using structured bindings \n";
for (const auto& [key, value] : map)
{
std::cout << "Key : " << key << ", value : " << value <<"\n";
}
std::cout << "\n";
return 0;
}

Problem in removing duplicate reverse lines from file

I have a file which contains the following lines,
connection list
current check OK
connect "A" to "B"
connect "A" to "C"
connect "A" to "D"
connect "C" to "A"
connect "A" to "E"
Here connect "C" to "A" is a reverse connection to connect "A" to "C"
The requirement is to remove the duplicate reverse connection.
I am new to C++ and vector. I tried using the following:
First I took a structure of 2 strings con1 and con2: connectPair
Then I took a vector of the structure
Now, I am saving the file lines to a vector: rawFileLines
I am trying to operate on rawFileLines to find the connection components.
I am storing the connection components to another vector: values
Here is my code:
typedef struct {
std::string con1;
std::string con2;
} ConnectPair;
void RemoveReversePairs(std::string inputFile) {
std::vector<std::string> fileData;
std::string line, scan, token1, token2;
std::size_t tokenLeft, tokenRight, maxLines, lineNumber = 0, pos = 0;
std::size_t found = 0, storeCount = 0;
std::vector<std::string> rawFileLines;
ConnectPair connectPair = {};
std::vector<ConnectPair> values;
std::ifstream source(inputFile.c_str());
while (std::getline(source, line)) {
rawFileLines.push_back(line);
}
source.close();
maxLines = rawFileLines.size();
for (size_t i = 0; i < maxLines; i++) {
line = rawFileLines[i];
pos = 0;
scan = "\"";
found = 0;
while (found < 2) /*line.find(scan, pos) != std::string::npos*/ {
tokenLeft = line.find(scan, pos);
tokenRight = line.find(scan, tokenLeft + 1);
if ((tokenLeft != std::string::npos) && (tokenRight != std::string::npos)) {
found++;
if (found == 1) {
connectPair.con1 = line.substr(tokenLeft + 1, (tokenRight - tokenLeft) - 1);
}
else if (found == 2) {
connectPair.con2 = line.substr(tokenLeft + 1, (tokenRight - tokenLeft) - 1);
values.push_back(connectPair);
storeCount++;
}
pos = tokenRight + 1;
}
else {
connectPair.con1 = "++";
connectPair.con2 = "++";
values.push_back(connectPair);
fileData.push_back(line);
break;
}
}
}
Now, I am having trouble comparing the connections. Please suggest me how to proceed.
Thank you.
Leaving the code to read in the connections to you as you mentioned in comments that that was working for you, consider an STL solution using the algorithms header's find_if along with a lambda.
For simplicity I have a std::vector<std::pair<std::string, std::string>> populated with your sample connections data.
I use a loop to print it to ensure the data is what I expect. This loop makes use of destructuring to cut out a lot of annoying boilerplate.
Then comes the real meat of the solution. We use an explicit iterator to loop over the vector, using std::find_if to check the rest of the vector for connections that are either identical, or are identical when reversed. If std::find_if returns the end iterator, it didn't find anything, and we can push that pair back onto the map2 vector. If an equivalent does exist in the rest of the vector, the current pair does not get pushed onto the map2 vector.
In the lambda it's important that we capture the current iter so we can compare it to the rest of them (represented by the argument to the lambda b).
[&iter](auto b) {
return (iter->first == b.first && iter->second == b.second) ||
(iter->first == b.second && iter->second == b.first );
}
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
int main() {
std::vector<std::pair<std::string, std::string>> map, map2;
map.push_back({"A", "B"});
map.push_back({"A", "C"});
map.push_back({"A", "D"});
map.push_back({"C", "A"});
map.push_back({"A", "E"});
std::cout << "Before:" << std::endl;
for (auto &[k, v] : map) {
std::cout << k << " -> " << v << std::endl;
}
auto end = map.end();
for (auto iter = map.begin(); iter != end; iter++) {
if (std::find_if(iter + 1, end,
[&iter](auto b) {
return (iter->first == b.first && iter->second == b.second) ||
(iter->first == b.second && iter->second == b.first );
}) == end) {
map2.push_back(*iter);
}
}
std::cout << "After: " << std::endl;
for (auto &[k, v] : map2) {
std::cout << k << " -> " << v << std::endl;
}
}
Result:
Before:
A -> B
A -> C
A -> D
C -> A
A -> E
After:
A -> B
A -> D
C -> A
A -> E
Better yet
After considering my previous example, I realized it can be simpler if we use the same comparison logic (the lambda is unchanged) to check if each connection already exists in map2.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
int main() {
std::vector<std::pair<std::string, std::string>> map, map2;
map.push_back({"A", "B"});
map.push_back({"A", "C"});
map.push_back({"A", "D"});
map.push_back({"C", "A"});
map.push_back({"A", "E"});
std::cout << "Before:" << std::endl;
for (auto &[k, v] : map) {
std::cout << k << " -> " << v << std::endl;
}
auto end = map.end();
for (auto iter = map.begin(); iter != end; iter++) {
if (std::find_if(map2.begin(), map2.end(),
[&iter](auto b) {
return (iter->first == b.first && iter->second == b.second) ||
(iter->first == b.second && iter->second == b.first );
}) == map2.end()) {
map2.push_back(*iter);
}
}
std::cout << "After: " << std::endl;
for (auto &[k, v] : map2) {
std::cout << k << " -> " << v << std::endl;
}
}
The other benefit of this is that we now get the first of the "duplicates" in map2 rather than the last.
Before:
A -> B
A -> C
A -> D
C -> A
A -> E
After:
A -> B
A -> C
A -> D
A -> E
Let me first say that I think that your code is somehow complicated.
Then, next. To remove the duplicates you can use the erase / remove_if idiom.
The code fragment that you need to put at the end of your function could be:
int i = 1;
while (i < values.size()) {
values.erase(std::remove_if(values.begin(), values.end(),
[&](const ConnectPair& cp)-> bool
{ return ((cp.con1 == values[i].con1) && (cp.con2 == values[i].con2)) || ((cp.con1 == values[i].con2) && (cp.con2 == values[i].con1)); }),
values.end());
++i;
}
Important is here the compare function. You make a 1 to 1 comparison and additionally you compare con1 with con2 and vice versa.
But let me say. Life can be easier. You can add alreadya compare function to your struct. That would be the more object oriented approach. And then you can use your struct in an appropriate container like std::set which will not allow duplicates.
And because we will not use a direction, but a connection, we can simply sort the first and the second element. This makes comparison ultra simple.
And the whole reading of data and doing all the task, can the be done in one line of code in main.
Please see:
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <regex>
#include <utility>
#include <set>
const std::regex re{ R"(\"(\w+)\")" };
struct Terminal {
// Store undirected connection in a noram std::pair
std::pair<std::string, std::string> end{};
// Read new connection from stream
friend std::istream& operator >> (std::istream& is, Terminal& t) {
bool found{};
// Read a line, until we found a connection or until eof
for (std::string line{}; not found and std::getline(is, line);)
// Get connection end names
if (std::vector ends(std::sregex_token_iterator(line.begin(), line.end(), re), {}); found = (ends.size() == 2))
t.end = std::minmax(ends[0], ends[1]);
return is;
}
bool operator < (const Terminal& other) const { return end < other.end; }
};
int main() {
// Open file and check, if it could be opened
if (std::ifstream inputFileStream{ "r:\\data.txt" }; inputFileStream) {
// Read complete data, without doubles into our container
std::set data(std::istream_iterator<Terminal>(inputFileStream), {});
// Debug output
for (const auto& d : data) std::cout << d.end.first << " <-> " << d.end.second << '\n';
}
}
Please note, if you need to original data, then one line with the original pair can be added to the struct.
Since your connection is unidirectional implicitly.
I'll suggest to use std::unordered_map<std::string,set::unordered_set<std::string>> if data has tons of connections to handle.
Because both unordered_map and unordered_set has constant time to lookup on average, but it takes longer time to insert.
I borrowed Chris's code to construct data.
Please note that Chris's example is good enough if your data is not large.
Live demo
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <unordered_set>
int main() {
std::vector<std::pair<std::string, std::string>> map;
map.push_back({"A", "B"});
map.push_back({"A", "C"});
map.push_back({"A", "D"});
map.push_back({"D", "A"});
map.push_back({"C", "A"});
map.push_back({"A", "E"});
map.push_back({"E", "A"});
std::cout << "Before:" << std::endl;
for (auto &[k, v] : map) {
std::cout << k << " -> " << v << std::endl;
}
std::unordered_map<std::string,std::unordered_set<std::string>> connection;
for (auto &[k, v] : map) {
// Existed connection
if((connection[k].find(v) != connection[k].end()) || (connection[v].find(k) != connection[v].end()) ){
continue;
}
connection[k].insert(v);
}
std::cout << "After: " << std::endl;
for (auto &[k, v] : connection) {
for(auto& item : v){
std::cout << k << " -> " << item << std::endl;
}
}
}

No matching member function for call to child.value

When I try to compile the code below I get an error:
src/main.cpp:51:48: error: no matching member function for call to 'child_value'
std::cout << "which has value" << eb.second.child_value(kv.second);
~~~~~~~~~~^~~~~~~~~~~
What I don't understand is I was able to use this in the loop above. I can only assume it wants me to use kv.second.child_value(kv.second); instead. However I want it to run this code on the xml returned by for (auto& eb: mapb) {.
#include "pugi/pugixml.hpp"
#include <iostream>
#include <string>
#include <map>
int main()
{
const std::map<std::string, std::string> tagMap {
{"description", "content"}, {"url", "web_address"}
};
pugi::xml_document doca, docb;
std::map<std::string, pugi::xml_node> mapa, mapb;
if (!doca.load_file("a.xml") || !docb.load_file("b.xml")) {
std::cout << "Can't find input files";
return 1;
}
for (auto& node: doca.child("data").children("entry")) {
const char* id = node.child_value("id");
mapa[id] = node;
}
for (auto& node: docb.child("data").children("entry")) {
const char* idcs = node.child_value("id");
if (!mapa.erase(idcs)) {
mapb[idcs] = node;
}
}
// For removed
for (auto& ea: mapa) {
std::cout << "Removed:" << std::endl;
ea.second.print(std::cout);
}
// For added nodes
for (auto& eb: mapb) {
// Loop through tag map
for (auto& kv : tagMap) {
// Try to find the tag name named in second map value
// and associate it to the type of information in first map value
std::cout << "Found" << kv.first;
std::cout << "which has value" << eb.second.child_value(kv.second);
}
}
}
If anyone could explain what I am doing wrong I would really appreciated it.
According to the two overloads found here
// Get child value of current node; that is, value of the first child node of type PCDATA/CDATA
const char_t* child_value() const;
// Get child value of child with specified name. Equivalent to child(name).child_value().
const char_t* child_value(const char_t* name) const;
you need to pass a pointer to a string (or string literal).
std::cout << "which has value" << eb.second.child_value(kv.second.c_str());
^^^^^^^^^^^^^^^^^

Passing map with custom comparator to function

I have an STL map with a custom comparator which I want to pass to a function, but the function doesn't recognize the custom comparator.
Trying to access the map within the main function works.
I have listed both attempts in my code.
#include <iostream>
#include <string>
#include <map>
// Error: cmpByStringLength is not recognized (both times)
void funcOut(std::map<std::string, int, cmpByStringLength> myMap)
{
for (std::map<std::string, int, cmpByStringLength>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
std::cout << it->first << " => " << it->second << std::endl;
}
}
int main()
{
// Reverse sort by length
struct cmpByStringLength {
bool operator()(const std::string& a, const std::string& b) const {
return a.length() > b.length();
}
};
std::map<std::string, int, cmpByStringLength> myMap;
myMap.emplace("String 1", 5);
myMap.emplace("String 123", 10);
funcOut(myMap);
// Working
for (std::map<std::string, int, cmpByStringLength>::iterator it = myMap.begin(); it != myMap.end(); ++it)
{
std::cout << it->first << " => " << it->second << std::endl;
}
return 0;
}
You can only use a name after its declaration, and only if it's in scope. Your comparator type is scoped within main, so you can only use it in that function. Move the definition out of main, into the global namespace (or in another namespace if you like), to make it available in other functions.
Alternatively, you could make the other function a template, so it can work with any map type:
template <typename Map>
void funcOut(Map const & myMap) {
// your code here
}
Use a template, because I'm a lazy c++ developer (I don't need to worry about lots of details...) I would do..
template <typename MapType>
void funcOut(MapType& myMap)
{
for (auto& p : myMap)
{
std::cout << p.first << " => " << p.second << std::endl;
}
}

how to assign or print out value for a vector in a structure?

Hey I have a structure
typedef struct CLUSTERINFO{
unsigned cluster;
vector <string> scopids;
}clusterinfo;
It looks like i have some issues to assign the value in to the vector scopids and then print them out
multimap<unsigned, clusterinfo> classinfomap;
clusterinfo clinfo;
string id_req;
//vector<unsigned> cluster_req_list and clustersipinfomap are some known from previous modules
for (ib=cluster_req_list.begin(); ib !=cluster_req_list.end(); ib++)
{
if(clustersipinfomap.count(*ib)>0)
{
cout<<count1<<"\t"<<*ib<<"\t";
clinfo.cluster= *ib;
std::pair<multimap<unsigned,sipinfo>::iterator, multimap<unsigned,sipinfo>::iterator> ret;
set<string>id_req_list;
id_req_list.clear();
ret=clustersipinfomap.equal_range(*ib);
//obtain the id_req_list
for (multimap<unsigned, sipinfo>:: iterator ic=ret.first; ic!=ret.second; ++ic)
{
string id_tmp=ic->second.id;
id_req_list.insert(id_tmp);
*****(clinfo.scopids).push_back(id_tmp); //i got sth wrong here
}
again sth is wrong for printing out the vector in the structure;
multimap<unsigned, clusterinfo>::iterator ip;
for(ip= classinfomap.begin(); ip!=classinfomap.end(); ip ++)
{
cout<<ip->first <<"\t"<< ip->second.cluster<<endl;
for (unsigned it=0; it< (ip->second.scopids).size(); it++)
{
count<< (ip->second.scopids)[it] << endl;
}
}
How to "assign" a value to a vector in a structure: you probably want to add an element to the vector, which you can achieve with std::vector::push_back:
struct Foo
{
std::vector<std::string> vec;
};
Foo f;
std::string s("Hello, World!";
f.vec.push_back(s);
How to print out contents of the vector?
C++11
for (const auto& e : f.vec)
std::cout << e << " ";
std::cout << std::endl;
C++03
for (std::vector<std::string>::const_iterator it = f.vec.begin(); it != f.vec.end(); ++it)
std::cout << *it << " ";
std::cout << std::endl;