Printing two vectors to screen in columns next to each other? - c++

So, my program uses a while loop to fill two separate vectors by asking the name of an item (vector one) then asking the price of the item (vector two).
double ItemCost;
vector<double> Cost;
string ItemName;
vector<string> Item;
while (ItemName != "done")
{
cout << "Item name: "; cin >> ItemName;
Item.push_back(ItemName);// gets item name from use and stores in the vector "Item"
if (ItemName != "done")
{
cout << "Item cost: $"; cin >> ItemCost;
Cost.push_back(ItemCost);// gets cost of item and stores it in the vector "cost"
}
else
{
continue;
}
}
system("CLS");
So after the clear screen, I would like the program to output a screen that shows the item name, then to the right it's cost. Then on the next line the same thing for the 2nd item input. Essentially like this would display:
cout << Item[0]; cout << " $" << Cost[0];
cout << Item[1]; cout << " $" << Cost[1] << endl;
cout << Item[2]; cout << " $" << Cost[2] << endl;
But, I want it to do this no matter how many items are input, also doing it the way I did above is obviously a bad Idea if they input less then the amount I have in the code, because the program will try to reach outside the vectors occupied memory ect. That was just to give an example of the format I'm going for.

If the vectors are the same size, you can use a simple for loop to iterate over the contents:
for (int i = 0; i < Item.size(); ++i) {
cout << Item[i] << " $" << Cost[i] << endl;
}
Before you run this code, you might want to check the two vectors have the same size using a debug assertion:
assert(Item.size() == Cost.size();
Alternatively, you could use perhaps something like std::min to only loop up the the smallest of the two vectors in case they are different sizes.

I would suggest using a std::vector of std::pair to store the price and item together this is simply because it makes keeping track of which price is associated with which item. This removes the need to check that both vectors are the same size and minimizes the chance for an error to occur. Then it is a simple matter of using a range based for loop to iterate through each pair and print them. I have shown an example below with a few improvements. There are a number of advantages of using std::pair over a simple struct, one such advantage is the inclusion of the necessary Boolean functions to be used in something like std::sort() Which if used would essentially sort your list of items alphabetically. This is very useful if you want to future proof your code.
double ItemCost;
string ItemName;
vector<std::pair<string, double> > Item;
while (ItemName != "done")
{
cout << "Item name: ";
// Using std::getline() as it terminates the string at a newline.
std::getline(std::cin, ItemName);
if (ItemName != "done")
{
cout << "Item cost: $"; cin >> ItemCost;
Item.push_back(std::make_pair (ItemName,ItemCost));
}
else
{
continue;
}
}
system("CLS");
// Printing here using c++11 range based for loop, I use this extensively
// as it greatly simplifies the code.
for (auto const &i : Item)
{
std::cout << i.first << "\t\t$" << i.second;
// ^^ Using double tab for better alignment
// instead of spaces.
}

Vector knows how big it is so there are a bunch of simple solutions, the easiest being
for (size_t index = 0; index < Item.size() && index < Cost.size(); index++)
{
cout << Item[index]; cout << " $" << Cost[index] << endl;
}
But a better idea is to have one vector that stores something like
struct ItemCost
{
string name;
double cost; // but it's generally safer to store money by pennies in integers
// You can't always count on floating point numbers in comparisons
}
This way Item and Cost can never get out of synch.
Read here for more on why floating point fails for precise things like money: How dangerous is it to compare floating point values?

Related

C++, an if loop for an assignment

I need some help trying to figure out a way to make an if statement taking the users input after a category is typed in. If the user types in string categories[0] in the cin statement, which in this case is "Number of drivers involved in fatal collisions per billion miles." I want it to display the first sentence from the worststate[0] array, which in this case will be North Dakota and South Carolina. I need help doing that for every category, to match up with the state listed, it goes in order.
#include <iostream>
using namespace std;
string worstState[7] = {
{"North Dakota and South Carolina"},
{"Hawaii"},
{"Montana"},
{"District of Columbia"},
{"District of Columbia and Mississippi"},
{"New Jersey"},
{"Louisiana"},
};
void displayIntro(){
cout << "Welcome to the Car Accident Statistic Website" << endl;
cout << "Take a look at the following categories: " << endl;
}
string categories[7] = {
{"Number of drivers involved in fatal collisions per billion miles"},
{"Percentage of drivers involved in fatal collisions who were speeding"},
{"Percentage of drivers involved in fatal collisions who were Alcohol-Impaired"},
{"Percentage of drivers involved in fatal collisions who were not distracted"},
{"Percentage of drivers involved in fatal collisions who had not been involved in any previous accidents"},
{"Car Insurance Premiums"},
{"Losses incurred by insurance companies for collisions per insured driver"},
};
int main(){
string Input;
displayIntro();
cout << categories[0] << endl;
cout << categories[1] << endl;
cout << categories[2] << endl;
cout << categories[3] << endl;
cout << categories[4] << endl;
cout << categories[5] << endl;
cout << categories[6] << endl;
cout << "Enter a category: ";
cin >> Input;
if (Input == categories....
return 0;
}
Obviously the code isn't the best organized, which I'm still gonna work on, but I just want the user to type in the category string, and for the state to match up with that specific category in the categories array, all in order based on the way they are entered.
Wondering what the best way to go about this situation is
You can use an unordered_map (aka hash table):
Initialize the map at the start of your program that maps all the questions to their respective answers (i.e. categories[i] to worstState[i]).
unordered_map<string, string> questionsToAnswers;
for (size_t i = 0; i < 7; i++) {
questionsToAnswers[categories[i]] = worstState[i];
}
When the user types a string in, you can look up the answer like this:
string s;
cin >> s;
auto itor = questionsToAnsers.find(s);
if (itor != questionsToAnswers.end()) {
cout << itor->second;
} else {
// question not found in map
}
If I understand correctly if the user inputs in categories[0], you want to display worstState[0], if the user inputs in catergories[1], you want to display worstState[1], and so on. Now it looks like your input is fixed which means you can use a switch statement, however because there are a fairly large number of courses you want to consider a for loop probably would look more clean.
So something like:
for(int i = 0; i < size(categories); i++){
if(Input == categories[i]){
cout << worstState[i] << endl;
}
}

Storing dynamic array in vector

I have to display a histogram of a student's grades. I've stored the grades in a dyn. array, but my goal is to store them in a vector. What's the right way of going about this? Hope this makes sense.
Edit:
My attempt at using a vector
void displayHistogram(int minGrade, vector<int> ptrV) {
cout << endl;
for (int i = 0; i <= minGrade; i++) {
if (ptrV[i] != 0) {
cout << "Number of " << i << "'s: " << ptrV[i] << endl;
}
}
}
void histogram() {
int minGrade = 0, grade;
const int grade_max = 100;
vector<int> ptrV(grade_max, 0);
cout << "Enter the student's grades (-1 to stop entering): \n";
do {
cin >> grade;
if (grade > minGrade) {
minGrade = grade;
}
if (grade >= 0) {
ptrV.push_back(grade);
}
} while (grade != -1);
displayHistogram(minGrade, ptrV);
}
Your basic mistake is that you try to force the vector as if it was a raw array. It does stuff for you, let it. It knows it's size, for instance. You don't need
void displayHistogram(int minGrade, vector<int> ptrV) {
cout << endl;
for (int i = 0; i <= minGrade; i++) {
Instead, you can use vector::size:
void displayHistogram(vector<int> ptrV) {
cout << endl;
for (size_t i=0; i<ptrV.size(); i++) {
(Even better: void displayHistogram(const vector<int>& ptrV) to signify that ptrV is not changed here and to avoid copying it every time you call the function by using a reference.)
(If you wouldn't use the i as it is the grade and if you have a newer compiler, I'd recommend a for each loop instead. Those are usually the way to go, just happens that you have one of the rarer cases in which it isn't.)
Likewise, you first set the size of your vector and then increase it, which to me means that you do not trust it:
vector<int> ptrV(grade_max, 0);
At this point, you have a vector with a hundred entries that are all zero. You don't need to resize it later if a hundred entries is all you need. vector::push_back resizes it. But note that setting it to a size of 100 means that [100] is not a valid position, the last one is [99], as we start to count at zero. You'd need to set it to a size of 101 to have both zero and hundred as valid addresses.
I'd change your code to:
const int grade_max = 100;
vector<int> ptrV(grade_max+1, 0); //changed it to +1 here as prtV[100] should be legal
cout << "Enter the student's grades (-1 to stop entering): \n";
while (true)
{
int grade; // put stuff in the smallest scope possible
cin >> grade;
if(grade == -1) break; // doing that here means we don't have to think about it anymore - the do while does it at last, I do it at first, handling all the special cases at the start and then assume I have the regular case.
if(grade < 0 or grade > grade_max) continue; // continue jumps to the top of the most inner loop. Note that I make sure to catch illegal but possible input.
ptrV[grade] += 1; // personal preference, I use ++ only to iterate
}
displayHistogram(ptrV);
I rewrote the structure, using a while(true), I think the way I did it is more intuitive, but there will be people who disagree with that and who would also write things like
if(grade == -1)
{
break;
}
and there are some good arguments for that, mostly a good practice routine, always doing the braces to avoid errors. However, I prefer a one liner to reduce verbosity.
One improvement would also be to tell the user about bad input:
if(grade < 0 or grade > grade_max)
{
cout << "Input not in valid range. Please choose number within 0 to " << grade_max << endl;
continue;
}
Now, another big thing to do here would be to leave the procedural part, by the way.
Go for a class GradeHistogram which has all those functions as a part of it, being called like
GradeHistogram histogram;
histogram.take_input();
histogram.display();
but that is for when you get your code working.
(My answer became more of a review as found on CodeReview, but I think that this is what you need rather than small fixes. This is something I can only recommend you, by the way, putting your code on CodeReview once it works.)
but my goal is to store them in a vector.
The issue seems to be that you've already sized the vector to hold grade_max entries. However when filling the vector, you are using push_back. By using push_back, you are adding more entries to the end of the vector, which is not what you want to do.
The solution is either
Change this vector<int> ptrV(grade_max, 0); to this vector<int> ptrV; and leave the calls to push_back alone, or
Keep vector<int> ptrV(grade_max, 0); but instead merely use ptrV[i] = grade;
If what you want to show is a histogram, then the easiest thing to do would be to use a std::map from grade to count of grade.
Something like this:
#include <iostream>
#include <map>
int main() {
std::cout << "Enter the student's grades (-1 to stop entering): \n";
std::map<int, int> grades_map;
int input_grade = -1;
do {
cin >> input_grade;
if (input_grade > -1) {
grades_map[input_grade]++;
}
} while (input_grade != -1);
// Print histogram
for (const auto& [grade, count] : grades_map) {
std::cout << "Students with grade << grade << ": ";
for (int i = 0; i < count; ++i) {
std::cout << '*';
}
std::cout << '\n';
}
}

How to print all values in a dynamically resizing struct? C++

I have a dynamically resizing struct[]. I ask the user for how many records he wants then make that many struct.Then I store the name and age info in the struct. The problem is when printing out that data i am only printing the last name and age the user input. I would like to print all the values.
struct Records
{
char name [32] ;
int age;
};
void program2()
{
Records rec;
int size;
cout << "How many record would you like?";
cin >> size;
cout << "\n" << size;
Records* a = new Records[size];
for (int i = 0; i < size; i++)
{
cout << "Whats the name for the Record: ";
cin >> rec.name;
cout << rec.name;
cout << "What is the age for this record: ";
cin >> rec.age;
}
for (int i = 0; i < size; i++)
{
cout << "\n" << rec.name << rec.age;
}
}
In your code, the variable rec has nothing to do with the array. To access the element at position i of your array, you'll need to use a[i].
For example:
cin >> a[i].name;
or
cout << "\n" << a[i].name << " " << a[i].age;
No surprise - you are actually printing repeatedly the last record that you created (rec). Your last loop is not actually traversing the array.
Also, the first loop only creates a struct, but does not actually add it to the array.
What you are trying to do is actually easier and cleaner if you use vectors instead of arrays, adding each record to the vector using push_back(). This is the closest thing to the "dynamically resized array" that you are referring to (such a thing does not actually exist in C++, that's what vectors are for, amongst other things).
Have a look into doing it this way, and if you get stuck feel free to ask again, happy to help.

C++ Array not taking correct input from file

Disclaimer: I am a beginner to programming, so what I say might sound really stupid
I have to make a "Telephone Directory" for school. The program isn't complete, but there are some things that I need to fix before moving on. The array TelephoneNumbers either isn't storing the numbers from the file correctly, or isn't displaying them. For the SeaerchRecords function, the first number in the file is displayed correctly, the second is displayed as "2147483647," and the rest of the numbers display as "0." The modify function also doesn't change the number, and I confirmed this with the while in the function. The string array works perfectly fine, however. May someone explain what I'm doing incorrectly?
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
string TelephoneNames[100];
int TelephoneNumbers[100];
void ModifyRecords(); //Function to Modify Records
void SearchRecords(); //Function to Search Records
void DeleteRecords(); //Function to Delete Records
int main()
{
fstream inputFile;
fstream outputFile;
char choice;
inputFile.open("Telephone Names.txt"); //To store
for (int count=0;count<100;count++) //file names
{ //into a
inputFile >> TelephoneNames[count]; //string
}
inputFile.close();
inputFile.open("Telephone Numbers.txt");//To store
for (int count=0;count<100;count++) //file #'s
{ //into a
inputFile >> TelephoneNumbers[count];//string
}
inputFile.close();
//Display options available
cout << " Hello, do you want to:\n";
cout << " ======================\n";
cout << "-Modify Records|Enter M\n";
cout << "-Search Records|Enter S\n";
cout << "-Delete Records|Enter D\n";
//Store choice
cin >> choice;
//Send to different function
if (choice=='M'||choice=='m')
{
ModifyRecords();
}
if (choice=='S'||choice=='s')
{
SearchRecords();
}
return 0;
}
void ModifyRecords()
{
string name;
string newname;
int newnumber;
int count=0;
cout << "Enter the name of the person: ";
cin >> name;
for (count=0;TelephoneNames[count]!=name;count++)//To determine where in the strings the new numbers need to be
{
}
cout << "Enter the new name of the person: ";
cin >> newname;
cout << "Enter the new number of the person: ";
cin >> newnumber;
TelephoneNames[count]={newname};
TelephoneNumbers[count]={newnumber};
count=0;
while (count<6)
{
cout << TelephoneNames[count] << endl;
cout << TelephoneNumbers[count] << endl;
cout << endl;
count++;
}
}
void SearchRecords()
{
string name;
int count=0;
cout << "Enter the name of the person you would like to find: ";
cin >> name;
for (count=0;TelephoneNames[count]!=name;count++)//To determine where in the strings the new numbers need to be
{
}
cout << "Name: " << TelephoneNames[count] << endl;
cout << "Number: " << TelephoneNumbers[count] << endl;
}
Since there is no any answer still and I don't see exactly the problem at this point I'll provide some suggestions how you can find a problem in your code.
In any programming situation when you can't find a bug, first task is to locate it as much precisely as you can and check all input data and assumptions. Usually, debugger is used for such purposes, but you can just output text in console before creating final version of your program.
To start with, you must check that you really received names and telephones from your file:
inputFile.open("Telephone Names.txt"); //To store
for (int count=0;count<100;count++) //file names
{ //into a
inputFile >> TelephoneNames[count]; //string
cout << TelephoneNames[count] << endl; //WE MUST SEE WHAT IS REALLY STORED IN TelephoneNames
}
inputFile.close();
inputFile.open("Telephone Numbers.txt");//To store
for (int count=0;count<100;count++) //file #'s
{ //into a
inputFile >> TelephoneNumbers[count];//string
cout << TelephoneNumbers[count] << endl; //WE MUST SEE WHAT IS REALLY STORED IN TelephoneNumbers
}
inputFile.close();
Ok, when it is checked and you are defenitely sure there is no problem in your data we can move to SeaerchRecords function doing the same procedure. We must check what is happening while you are searching:
for (count=0;TelephoneNames[count]!=name;count++)//To determine where in the strings the new numbers need to be
{
cout << "Search step: " << count << " name " << name << " found name " << TelephoneNames[count] << " number " << TelephoneNumbers[count] << endl;
}
Doing so you will locate your bug rather quickly. The problem can be in input files format, in difference of "name" and stored names format etc.
I'll provide several additional suggestion how you can improve your code.
1) Try to use const declarations for such commonly used things as number of records (const int NUMBER_OF_RECORDS = 100; insted of just putting '100' everywhere), it will reduce the amout of work and possible bugs. 2) Try to check all possible problems that you program can encounter if someting is wrong with data. What will happen if you have less than 100 records in your files now? Program crush or silent reading of unappropriate data which is even worse. Check that you haven't reach file end on any step of reading along with current check that you've reached you number of records and do something in case of unappropriate data.
3) Check the possible problems with conditions in your cycles not to run them infinite number of times. Now your condition for(count=0;TelephoneNames[count]!=name;count++)
will execute forever if there is no such name or just crush the program on count 100 or more. You should check that count doesn't exceed that value. Good luck!

C++ How to return multiple arrays of different types from a function

I am trying to write a function that accepts 3 different arrays. These arrays are of type string, double, and double, respectively. The function will fill these arrays with data and then return them to main. However, I am unsure what to declare as the return type for the function, as all of the arrays do not hold the same data types. The function that will accept the arrays as arguments is listed below
void additems(string arry1[], double arry2[], double arry3[], int index)
{
/************************* additems **************************
NAME: additems
PURPOSE: Prompt user for airport id, elevation, runway length. Validate input and add to 3 seperate parallel arrays
CALLED BY: main
INPUT: airportID[], elevation[], runlength[], SIZE
OUTPUT: airporID[], elevation[], runlength[]
****************************************************************************/
//This function will prompt the user for airport id, elevation, and runway length and add them to
//separate parallel arrays
for (int i=0; i<index; i++)
{
cout << "Enter the airport code for airport " << i+1 << ". ";
cin >> arry1[i];
cout << "Enter the maximum elevation airport " << i+1 << " flys at (in ft). ";
cin >> arry2[i];
while (arry2[i] <= 0)
{
cout << "\t\t-----ERROR-----";
cout << "\n\t\tElevation must be greater than 0";
cout << "\n\t\tPlease re enter the max elevation (ft). ";
cin >> arry2[i];
}
cout << "Enter the longest runway at the airport " << i+1 << " (in ft). ";
cin >> arry3[i];
while (arry3[i] <= 0)
{
cout << "\t\t-----ERROR-----";
cout << "\n\t\tRunway length must be greater than 0";
cout << "\n\t\tPlease re enter the longest runway length (ft). ";
cin >> arry3[i];
}
cout << endl;
}
return arry1, arry2, arry3;
}
Thanks in advance for considering my question
You do not need to return the arrays, as they are modified by the function. When you pass arrays to a function like this, the array is passed by reference. Normally a data type is passed by value (ie copied), but arrays are treated a little like pointers.
So just return void, or if you like you could return some kind of value to indicate success (if that's appropriate). You may want to return an integer to say how many records were entered (if the user has the option to enter less than index records).
You can just say
return;
or leave it out altogether. You're modifying the passed-in arrays in-place, so there's no need to return anything.