So, we have a school-project in creating a phonebook where you should be able to look up phone numbers by searching for the name. I decided to use a map with a string for the phone number and and a vector of strings for the name, due associated number should be able to have multiple names in it.
However, due to us jumping straight from Python to C++ without any explanation of the syntax or the language, I am having a hard time coming up with a way to look for the number by searching for names.
The class I am using looks like this
class Telefonbok
{
public:
void add(string namn, string nummer)
{
map<string, vector<string>>::iterator it = boken.find(nummer);
if (it != boken.end())
{
cout << "This number already exists, please choose another";
}
else
{
namn_alias.push_back(namn);
boken[nummer] = namn_alias;
}
}
void lookup(string name)
{
for (map<string, vector<string>>::iterator sokning = boken.begin(); sokning != boken.end(); sokning++)
cout << "Hello!";
}
private:
vector<string> namn_alias;
string nummer;
map<string, vector<string>> boken;
};
What I am trying to do in lookup function is to search for a phone number by the names in the vector, but I am stumped on how to proceed with looking through the vector inside the for-loop.
The plan was to go through the Map keys one by one to find the vector that contains the searched-for name. Any tips on how to proceed or some functions I have missed that can be used for this?
Algirdas is correct, you should read up on C++.
Assuming you are mapping name to 1-or-more numbers, but only 1 number per name...
#include <cstddef>
#include <iostream>
#include <map>
#include <string>
#include <vector>
using std::cout;
using std::endl;
using std::map;
using std::string;
using std::vector;
class Telefonbok
{
public:
void add(string namn, string nummer) {
auto it = nummer_namn.find(nummer);
if (it != nummer_namn.end()) {
cout << "This number already exists, please choose another" << endl;
}
else {
nummer_namn[nummer] = namn;
namn_nummer[namn].push_back(nummer);
}
}
void lookup(string name) {
auto it = namn_nummer.find(name);
if (it == namn_nummer.end()) {
cout << "Unable to find any numbers for " << name << ", sorry." << endl;
return;
}
for (auto const& sokning : it->second)
cout << name << " : " << sokning << endl;
}
private:
map<string, vector<string>> namn_nummer;
map<string, string> nummer_namn;
};
int main() {
Telefonbok bok;
bok.add("Eljay", "789");
bok.add("Eljay", "456");
bok.add("Beaker", "123");
bok.lookup("Eljay");
bok.lookup("Beaker");
bok.lookup("Bunsen Honeydew");
return EXIT_SUCCESS;
}
Related
I'm trying to write a program that first checks if a name is in a vector and if not then adds it to the vector. My code seems to have difficulties with parsing, at least that's what I get out of it. I tried changing the string to a char but it did not help me much.
#include <iostream>
#include <vector>
#include <string>
bool isinVector(std::string uElement, std::vector<std::string> uArray)
{
for (unsigned int i = 0; i <= sizeof(uArray); i++) {
if (uArray[i] == uElement) {
return true;
}
else {
return false;
}
}
}
int main()
{
bool trigger = false;
while (!trigger) {
std::vector<std::string> names;
names.push_back("Bart");
std::string newName;
getline(std::cin, newName);
if (isinVector(newName, names))
{
std::cout << "true" << std::endl;
trigger = true;
}
else
{
std::cout << "false" << std::endl;
names.push_back(newName);
for (int i = 0; i <= sizeof(names); i++) {
std::cout << names[i] << std::endl;
}
}
}
}
I made some adjustments to your code, removing your isinVector function and using a lambda inside the main function instead. In the future please provide a concise question and example.
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using std::vector;
using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::find_if;
int main(){
bool trigger = false;
while (!trigger) {
vector<string> names;
names.push_back("Bart");
string newName;
getline(cin, newName);
if(find_if(names.begin(), names.end(), [newName] (const string& name){
return !name.compare(newName);
}) != names.end()){
cout << "true" << endl;
trigger = true;
}
else{
cout << "false" << endl;
names.push_back(newName);
for (size_t i = 0; i < names.size(); i++) {
cout << names.at(i) << endl;
}
}
}
return 0;
}
The code uses std::find_if to check if the element exists in the vector. If std::find_f does not return the iterator to uArray.end() Then the element exists. Also your for loop used sizeof which is incorrect, use the vector.size method. And you were looping until <= , it should be < uArray.size() And it's safer to access elements in the vector through the .at method rather than an index [] since the .at will throw an out_of_range exception.
Among the things wrong in the updated post.
Improper use of sizeof
Reinventing a standard algorithm
Lack of error checking
Consider the tasks you're trying to accomplish. You want to:
Initialize a starting vector containing the name Bart
Continuously read new names. For each new name read:
a. Check to see if it is already in the vector.
if it is present terminate the read loop
else add it to the vector, and print the entire vector
This sequence of operations can be accomplished with stepwise refinement.
Step 1. Read names
First, you need to be able to continuously read names:
#include <iostream>
#include <string>
int main()
{
std::string name;
while (std::getline(std::cin, name))
std::cout << name << '\n';
}
Simple enough. Running this will echo any strings you type, one at a time, separated by newlines.
Step 2. Accumulate names in a vector
Next, we need to add a vector to hold the strings we're reading, with an initial population of the name "Bart". For this pass we'll be just putting every string we read into the vector
#include <iostream>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> names = { "Bart" };
std::string name;
while (std::getline(std::cin, name))
{
names.emplace_back(name);
for (auto const& s : names)
std::cout << s << ' ';
std::cout.put('\n');
}
}
In addition to what was done prior, we're now accumulating strings in the vector, including duplicates, and reporting the vector content after each name read. This gets us closer to our stated goal.
Step 3: Conditional loop exit based on duplicate detection
Now we need to check for duplicates, and terminate the loop once it happens. We can do this using std::find. The final code is below:
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
int main()
{
std::vector<std::string> names = { "Bart" };
std::string name;
while (std::getline(std::cin, name))
{
if (std::find(names.begin(), names.end(), name) != names.end())
break;
names.emplace_back(name);
for (auto const& s : names)
std::cout << s << ' ';
std::cout.put('\n');
}
}
That's it. This is a simple task, but it lends itself nicely to an example of how you break a multi-part task down to manageable objectives , then build it in pieces.
Hope you found it useful.
Now my code looks like this:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
bool isinVector (std::string uElement, std::vector<std::string> uArray) {
bool invector = false;
std::vector<std::string>::iterator it = std::find(uArray.begin(),
uArray.end(),uElement);
if(it != uArray.end()){
invector = true;
}
return invector;
}
int main(){
bool trigger = false;
std::string name;
std::vector<std::string> names = { "Bart" };
while (std::getline(std::cin, name)){
if (isinVector(name, names)) {
std::cout << "true" << std::endl;
break;
}
else
{
std::cout << "false" << std::endl;
names.emplace_back(name);
}
}
return 0;
}
and it works, thanks a lot guys!
I need to find all the keys in the kTypeNames[] with rapidJSON library.
Trying to iterate all the nodes but I'm missing something; here's the code:
#include <iostream>
#include <fstream>
#include <string>
#include <bits/stdc++.h>
#include <unistd.h>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;
using namespace std;
const char* kTypeNames[] = { "id", "text", "templ_text", "key" };
int main(int argc, char* argv[]) {
string line;
char json[65000];
std::ifstream file(argv[1]);
unsigned long i = 0;
if (file.is_open()) {
while (!file.eof()) {
file.get(json[i]);
i++;
}
file.close();
} else {
cout << "Unable to open file";
}
Document document;
document.Parse(json);
printf("\n\n\n\n*********Access values in document**********\n");
assert(document.IsObject());
for (auto Typename : kTypeNames) {
if (document.HasMember(Typename)) {
cout << "\n";
cout << Typename << ":" << document[Typename].GetString()<< endl;
cout << "\n";
}
else {
cout << "\n None\n";
}
}
It does not works with a nested JSON.
{
"node": {
"text": "find this",
"templ_text": "don't find",
"ver": "don't find"
},
"ic": "",
"text": "also this",
"templ_text": "don't care",
"par": {
"SET": {
"vis": "<blabla>",
"text": "keyFound",
"templ_text": "don't need this"
}
}
}
This is the output:
None
text:also this
templ_text:don't care
None
I would like to find all the "text" keys
How can I iterate through all the nodes/ json document?
The code you have is just searching for a list of pre-defined keys directly within the document root (document.HasMember is not a recursive search!).
You could just loop through the document nodes recursively. For example for object/map nodes, you loop on the MemberBegin() and MemberEnd() iterators, similar to a std::map or other standard containers.
for (auto i = node.MemberBegin(); i != node.MemberEnd(); ++i)
{
std::cout << "key: " << i->name.GetString() << std::endl;
WalkNodes(i->value);
}
Array uses Begin() and End(). Then, when you encounter a node with a "text" member, you can output the value of that node (i->value).
Alternatively, rather than using a Document DOM object, you can do it with the parser stream. Rapidjson uses a "push" API for this, where it calls methods you define in a class as it encounters each piece of JSON. Specifically, it will call a Key method.
class MyHandler : public BaseReaderHandler<UTF8<>, MyReader> {
bool Key(const char* str, SizeType length, bool copy)
{
std::cout << "Key: " << str << std::endl;
}
...
};
MyHandler handler;
rapidjson::Reader reader;
rapidjson::StringStream ss(json);
reader.Parse(ss, handler);
This gets a bit more complex, you will want to set a flag of some sorts, and then output the next value callback after.
class MyHandler : public BaseReaderHandler<UTF8<>, MyReader> {
bool Key(const char* str, SizeType length, bool copy)
{
isTextKey = strcmp(str, "text") == 0; // Also need to set to false in some other places
return true;
}
bool String(const char* str, SizeType length, bool copy)
{
if (isTextKey) std::cout << "text string " << str << std::endl;
return true;
}
...
bool isTextKey = false;
};
Also remember, that JSON allows a null within a string \0, which is why also have the size parameters and members, as well as Unicode. So to fully support any JSON document that needs accounting for.
I want to be able to create and store values by using the console and getting user input to create those values. So I would like to be able to type in the console something like
1234
123
1
and it would save it into my map with a tuple such as
std::map<int, std::tuple<int, int>> info;
info[1234] = { 123, 1 };
I am completely new to this and have been looking up stuff for a couple hours but I do not understand how to use << >> everywhere I look says to use those. I would like to be able to close the program and open it and the values would still be stored as well.
Any and all information would be appreciated. Thanks.
I've put together some sample code to approximate your task. It reads some integers from standard input, and throws it back to standard output. For your purposes, you may wish to format the output differently, and read/save it from/to a file at some point. There is probably more terse, more robust, more efficient, and prettier ways to do it, but I put this together quickly to get you started. If you find things that are strange to you, I would be happy to answer some questions, but the idea of this site is that you prove that you put some work into finding out the answers first (for example by visiting other questions, reading some books, or consulting the C++ reference at: https://en.cppreference.com/w/cpp)
#include <iostream>
#include <map>
#include <stdexcept>
using namespace std;
using MyMap = map<int, pair<int, int>>;
MyMap::value_type read_entry() {
int key, v_1, v_2;
bool started_read{false};
if ((cin >> key) && (started_read = true) && (cin >> v_1) && (cin >> v_2)) {
return {key, {v_1, v_2}};
} else if (started_read) {
throw invalid_argument("improper input");
} else {
return {};
}
}
MyMap read_map() {
MyMap myMap;
while (true) {
auto entry = read_entry();
if (cin) {
myMap.insert(move(entry));
} else if (cin.eof()) {
return myMap;
} else {
throw invalid_argument("io error");
}
}
}
void dump_map(const MyMap &myMap) {
for (auto &&value : myMap) {
cout << value.first << "\n"
<< value.second.first << "\n"
<< value.second.second << endl;
}
}
int main() {
cout << "reading map..." << endl;
MyMap myMap;
try {
myMap = read_map();
} catch (invalid_argument e) {
cout << "error encountered reading map: " << e.what() << endl;
return 1;
}
cout << "dumping map..." << endl;
dump_map(myMap);
}
I'm trying to test C++ map::erase() with the following code:
//file user.h
#include <string>
#include <fstream>
#include <cstring>
using namespace std;
class User {
string name;
int id;
public:
User(const string& name, int id) : name(name), id(id) {}
int getID() const {return id;}
~User(){}
};
//file main.cpp
#include "user.h"
using namespace std;
typedef map<string, User*> Dict;
int main()
{
Dict dict;
dict["Smith"] = new User("Smith", 666); //Id = 666
dict["Adams"] = new User("Adams", 314); //Id = 314
auto it = dict.find("Adams"); //look for user 'Adams'
if (it == dict.end())
//show 'not Found' if didn't find 'Adams'
cout << "not Found" << endl;
else
//else, show the Id = 314
cout << "id1: " << it->second->getID() << endl;
//Here I think there is a problem
//I ask to delete Adams from the list
dict.erase(it);
//So in this print the ID shouldn't be found
cout << "id2: " << it->second->getID() << endl;
return 0;
}
After I try to delete the item from the list it seems like it is not deleted as the program shows the following:
pc#pc:~/Test$ ./main
id1: 314
id2: 314
As I understand id2 shouldn't show any value. Is this good or did I misunderstood the use of erase. If yes, how can I delete the item after it is shown?
you are in undefined behavior land. You are using an iterator (it) after you have modified the map. Anything can happen - including apparently working (a bit). You shoud redo
auto it = dict.find("Adams"); //look for user 'Adams'
this will not find anything
Basically you have undefined behavior calling
dict.erase(it);
//So in this print the ID shouldn't be found
cout << "id2: " << it->second->getID() << endl;
The iterator variable isn't somehow reset when it was used with dict.erase(it);.
Also you should take care to call delete before using erase(). Otherwise you would leak memory.
You're erasing a pointer from the map, but the object being pointed to by the map isn't being erased. You need to spend some time learning about memory management in c++.
I have a class that contains 3 elements for example {first_name, Last_name, Phone}
I have a vector that holds this set of information. In what manner could I go about looking for a single element of the set, for example find(last_name), and delete all elements that contain that specific last name?
I've tried many examples and have searched far and wide throughout the world wide google. Please help. Attached is bits of code:
int number = 4;
vector <Friend> BlackBook(number);
Friend a("John", "Nash", "4155555555");
Friend d("Homer", "Simpson", "2064375555");
BlackBook[0] = a;
BlackBook[1] = d;
Now that's just same basic code for the set up. Here's a couple of things i've tried. But the more I look at what the code says, the more it seems as if it's not allowing for a string argument... but then i don't know how to give a class arguement with respect to a specific string... well I don't know what i'm doing wrong. I have a feeling I could do this with pointers, but the whole pointer thing isn't clicking yet. But heres some things i've tried.
vector <Friend> :: iterator frienddlt;
frienddlt = find (BlackBook.begin(), BlackBook.end(), nofriend);
if (frienddlt != BlackBook.end())
{
BlackBook.erase( std::remove( BlackBook.begin(), BlackBook.end(), nofriend), BlackBook.end() );
}
else
{
cout << nofriend <<" was not found\n" << "Please Reenter Last Name:\t\t";
}
When I compile the project the header file stl_algo.h opens and points to line 1133.
Any Help would be much appreciated!! thank you!
Try remove_if
My example:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
struct Friend {
string first_name;
string last_name;
string phone;
};
bool RemoveByName (vector<Friend>& black_book, const string& name) {
vector<Friend>::iterator removed_it = remove_if(
black_book.begin(), black_book.end(),
[&name](const Friend& f){return f.first_name == name;});
if (removed_it == black_book.end())
return false;
black_book.erase(removed_it, black_book.end());
return true;
}
int main() {
vector <Friend> black_book {
Friend {"John", "Nash", "4155555555"},
Friend {"Homer", "Simpson", "2064375555"}
};
if (RemoveByName(black_book, "John")) {
cout << "removed" << endl;
} else {
cout << "not found" << endl;
}
if (RemoveByName(black_book, "Tom")) {
cout << "removed" << endl;
} else {
cout << "not found" << endl;
}
for (int i = 0; i < black_book.size(); ++i) {
Friend& f = black_book.at(i);
cout << f.first_name << " " << f.last_name << " " << f.phone << endl;
}
return 0;
}
Output:
removed
not found
Homer Simpson 2064375555
Of course, you can always loop over all Friend elements and delete them manually.
Blackbook::iterator friend = Blackbook.begin();
while (friend != Blackbook.end())
{
if (friend->last_name == bad_name)
{
friend = Blackbook.erase(friend);
}
else
{
++friend;
}
}