Reputation: 449
Goal: Read numerical text files into vectors and then add the vectors to key,value std::map
so that I can reference them by the key name I have specified for them, later.
Thought this would be easy and I am surprised that I can't find an answer for this already on StackOverflow.
Result Expected:
Print1 = {100,200,500,600}
Print2 = {7890,5678,34567,3,56}
Print3["NameA"] = Print1
Print3["NameB"] = Print2
If my process is inefficient or going in the wrong direction, I would appreciate the pointers.
I keep getting Semantic Issue build fails and no viable conversion from pair <const basic_string>
Current Code:
#include <string.h>
#include <iostream>
#include <map>
#include <utility>
#include <vector>
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;
}
int main()
{
std::vector<int> print1;
std::ifstream inputFile("numbers.txt");
// test file open
if (inputFile)
{
double value;
// read the elements in the file into a vector
while ( inputFile >> value ) {
print1.push_back(value);
}
}
inputFile.close();
std::vector<int> print2;
std::ifstream inputFile2("numbers2.txt");
// test file open
if (inputFile2)
{
double value;
// read the elements in the file into a vector
while ( inputFile2 >> value ) {
print2.push_back(value);
}
}
inputFile2.close();
std::map<std::string, std::vector<int>> contacts;
contacts["alice"] = print1;
contacts["bob"] = print2;
std::vector<std::string> keys(contacts.size());
std::vector<int> values(contacts.size());
transform(contacts.begin(), contacts.end(), keys.begin(), key);
transform(contacts.begin(), contacts.end(), values.begin(), value);
std::cout << "Keys:\n";
copy(keys.begin(), keys.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
std::cout << "\n";
std::cout << "Values:\n";
copy(values.begin(), values.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
Upvotes: 3
Views: 3609
Reputation: 6240
You can reference map element directly which will create an entry if doesn't exist, and just fill it from the file read loop:
#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <string>
int main()
{
std::map<std::string, std::vector<int>> m;
int num;
auto &&alice = m["alice"];
std::ifstream if_alice("numbers1.txt");
while (if_alice >> num)
alice.push_back(num);
if_alice.close();
auto &&bob = m["bob"];
std::ifstream if_bob("numbers2.txt");
while (if_bob >> num)
bob.push_back(num);
if_bob.close();
// test
for (auto &&data : m)
{
std::cout << "Name: " << data.first << "\t";
for (int num : data.second)
std::cout << num << " ";
std::cout << "\n";
}
}
Upvotes: 1
Reputation: 32722
First of all, there is no point in arguing that Xcode didn't show any error msgs for your code. Try either turning on all the compiler warnings or try in online compilers. The result will be not disappointing: https://godbolt.org/z/cU54GX
If I have understood correctly, you want to store your information from two files(the integer values) in a std::map
, where its key = std::string
and value = vector of integer array
.
If so,
1. You have your problem starting from reading integers from files.
There you are using double
for no reason and storing to
std::vector<int>
(i,e print1
and print2
).
2. Secondly, what if your file has not been open?. inputFile.close();
and inputFile2.close();
will close it anyway, without knowing the
fact. This is wrong.
The proper way would be:
inputFile.open("numbers.txt", std::ios::in); // opening mode
if (inputFile.is_open()) {
// do stuff
inputFile.close(); // you need closing only when file has been opened
}
3. If your intention is to only print keys
and values
, you don't
need to parse them to different vectors.
You can do it directly:
for(const std::pair<kType, vType>& mapEntry: contacts)
{
std::cout << "Key: " << mapEntry.first << " Values: ";
for(const int values: mapEntry.second) std::cout << values << " ";
std::cout << std::endl;
}
In c++17 you can use Structured binding
for(const auto& [Key, Values]: contacts)
{
std::cout << "Key: " << Key << " Values: ";
for(const int value: Values) std::cout << value << " ";
std::cout << std::endl;
}
4. If you really want to parse them to a different vector; first of all, the data structure for storing keys
is wrong:
std::vector<int> values(contacts.size());
^^^^^^
which should have been a vector of vector of integers, as your vType = std::vector<int>
. That is,
std::vector<std::vector<int>> values
^^^^^^^^^^^^^^^^^^
Secondly, you have the functions key(const std::pair<std::string, std::string>& keyValue)
and value(const std::pair<std::string, std::string>& keyValue)
wrong, where you are passing a pair of strings as keys and values.
This should have been
typedef std::string kType; // type of your map's key
typedef std::vector<int> vType;// value of your map's value
std::pair<kType, vType>
However, you can simply replace with lambdas, which would be more intuitive, in the sense of having the functions next to the line where you needed. For example,
std::vector<kType> keysVec;
keysVec.reserve(contacts.size());
auto getOnlyKeys = [](const std::pair<kType, vType>& mapEntry){ return mapEntry.first; };
std::transform(contacts.begin(), contacts.end(), std::back_inserter(keysVec), getOnlyKeys);
Upvotes: 1