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';
}
}
Related
I'm working on an assignment right now and when run my code returns this error:
main.cpp:60:20: error: ‘dataArr’ was not declared in this scope
if(tolower(dataArr[i].last) == tolower(lastName))
I'm not quite sure what I'm missing here. If I could at least get it to run I'd appreciate it. Thanks.
I thought arrays were declared globally so i thought it wouldn't be an issue in my functions
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct Database
{
string first;
string last;
string ID;
string phoneNum;
};
void lastSearch(string);
void idSearch(string);
int main()
{
Database dataArr[100];
ifstream myFile("library_database.txt");
int count = 0;
while(!myFile.eof() && count < 100)
{
myFile >> dataArr[count].first >> dataArr[count].last >> dataArr[count].ID >> dataArr[count].phoneNum;
cout << dataArr[count].first << " " << dataArr[count].last << " " << dataArr[count].ID << " " << dataArr[count].phoneNum << endl;
count++;
}
int input;
string search;
cout << "Would you like to search by last name or member ID?\n1. Last Name\n2. ID\n> ";
cin >> input;
while(input != 1 || input != 2)
{
cout << "Enter a valid answer.\n> ";
cin >> input;
}
if(input == 1)
{
cout << "Enter last name: ";
cin >> search;
lastSearch(search);
}
if(input == 2)
{
cout << "Enter ID: ";
cin >> search;
idSearch(search);
}
return 0;
}
void lastSearch(string lastName)
{
int num = 0;
for(int i = 0; i < 100; i++)
{
if(tolower(dataArr[i].last) == tolower(lastName))
{
cout << dataArr[i].first << " " << dataArr[i].last << " " << dataArr[i].ID << " " << dataArr[i].phoneNum << endl
num++;
}
}
if(num == 0)
{
cout << "No match was found in the file.";
}
}
voidSearch was removed to allow this to be posted
To answer the title of your post: because it isn't.
You declare dataArr in main, but you are trying to use it in lastSearch, so lastSearch can't see it. But you can pass it in as a parameter, that's probably the easiest fix:
void lastSearch(const string lastName, const Database *dataArr) { ... }
and call it like this:
lastSearch (search, dataArr);
Note the use of const (get into the habit of doing that whenever you can) and that your array 'decays' to a pointer when you pass it as a parameter like this, so don't be tempted to use sizeof in lastSearch. If you need to know the number of elements in the array, pass that as a parameter too.
Or, better, use std::array instead of a C-style array and then the size of the array is available in lastSearch without the need to pass it in separately. If you do that, you probably want to pass it by const reference to avoid copying it every time you call the function.
Finally, it might be time to learn about std::vector. At the expense of a little more complexity (but not much), this would avoid the need to allocate a fixed size array. Again, for the same reason, pass it around by reference.
Some bedtime reading: The Definitive C++ Book Guide and List
Arrays are not declared globally, they are declared where you declare them :-)
In your case, you declare it at the top of main() so that is its scope, from point of declaration to end of main(). Trying to use it in lastSearch() is therefore invalid.
The easiest fix is probably just to move the declaration immediately before main() so that it is global. But the easiest things is often not the right thing.
You would be better off embracing C++ fully(1) and using something like std::vector, whose size isn't arbitrarily limited to 100 (for example) and which you could pass around quite easily, something like:
#include <iostream>
#include <vector>
void function(const std::vector<int> &vec) {
std::cout << vec.size() << ' ' << vec[0] << '\n'; // Output: 2 42
}
int main() {
std::vector<int> x;
x.push_back(42);
x.push_back(99);
function(x);
}
The main advantages with vectors are that:
you're not limited to a maximum of 100 items;
you don't have to pass around the actual count of items read separately as with a raw array or even a std::array (you don't do that in your code but I assure you, that's a problem).
the size of the vector is an integral property of the vector, available anywhere the vector is in scope.
(1) There's a variety of developers I like to call C+ developers. These are the people that, though they claim to be C++ developers, have never really embraced the C++ way of doing things, sticking to C style programming practices like non-smart pointers or normal arrays :-)
Some of those things may still have a place in modern C++ code but you should be circumspect in their use.
I wanted to write a code where user will give input the element of the array and then the elements will be print as an array. Here is my code but the code do not give the array as output.
#include<iostream>
using namespace std;
int main(){
int arr[100] , size , i , num ; //Here we are defining the maximum size of array is 100. So user can choose the size of array by him/her but cannot choose more than 100
cout << "Enter the size of array (Maximum array size you can take is 100) : ";
cin >> size;
if (size > 100)
{
cout << "You cannot take the size more than 100" << endl;
}else{
cout << "Inter the elements using space : ";
for (int i = 0; i < size; i++)
{
cin >> arr[i];
}
cout << "Enter data you want to insert : ";
cin >> num;
for (int i = size - 1 ; i >= 0 ; i--)
{
arr[i+1] = arr[i];
}
arr[0] = num;
size++;
}
cout << arr[i] << endl;
return 0;
}
Your question isn't entirely clear, but I see two basic problems.
First, you define variable i at the top of your code. That's fine, although there are arguments for variable names being longer than a single character. Think about searching for uses of that variable -- you're going to get it in all sorts of places that have nothing to do with the variable. while has an i. if has an i. Be that as it may.
But here's a real problem. You have some for loops like this:
for (int i = 0; ....)
There's nothing wrong with that, not exactly. It works. HOWEVER, it's considered bad form to reuse a variable inside an inner block that matches a variable from an outer block. It's legal, but it's a common source of bugs. I recommend you don't do it.
Then, at the bottom, you do this:
cout << arr[i] << endl;
At this point, we're back to the original variable i that you declare at the top. But you never actually initialize it, so it's some random value. And you're not doing any sort of loop.
I suspect if you wrap this inside another of your for-loops, you'd get the results you want.
And get rid of declaring i at the top.
This question already has answers here:
What is a debugger and how can it help me diagnose problems?
(2 answers)
Closed 4 years ago.
The goal of this code is to pass an array through a function (which I'm already having a difficult time understanding). I went through with a pen and paper and traced the code and I think I just don't know enough to understand what's going wrong. All the test scores I throw in just push back a ridiculously large negative number. I'm not asking for you guys to do my homework because I really want to try and understand what I'm doing, but any help would really be appreciated right now.
#include <iostream>
//function prototype
double average(int studentScores[], int size);
double studentScores[4];
bool runAgain(void);
int main() {
do {
int studentScores[4], size = 4, result;
string score;
cout << "This program will calculate the average of four diffrent exam scores." << endl;
for (int i = 0; i < size; i++) {
studentScores[i] = 0;
cout << "Please Enter Exam Score " << i + 1 << ": ";
getline(cin, score);
}
for (int i = 0; i < size; i++) {
result = (studentScores[1] + studentScores[2] + studentScores[3] + studentScores[4]) / size;
studentScores[i]++;
}
cout << "The Average Exam score is " << result << endl;
} while (runAgain());
system("pause");
return 0;
}
//function implementation
double average(int studentScores[], int size) {
for (int i = 0; i < size; i++) {
return (studentScores[i]++ / size);
}
}
bool runAgain(void) {
char userResponse;
cout << "\nWould you like to run again (y or n): ";
cin >> userResponse;
if (userResponse == 'y' || userResponse == 'Y')
return(true);
return(false);
}
First obvious bug:
int studentScores[4]
studentScores has 4 elements, numbered studentScores[0] through studentScores[3].
But your code accesses studentScores[4] in
result = (... + studentScores[4]) / ...
which doesn't exist (and doesn't access studentScores[0], which does).
Glad to try to help ya out without giving you the answer.
I'm gonna sprinkle some questions throughout your code that you should be asking yourself in future programs whenever you get unexpected output.
#include <iostream>
//function prototype
double average(int studentScores[], int size);
double studentScores[4];
bool runAgain(void);
int main() {
do {
int studentScores[4], size = 4, result;
string score;
/* Above, you declared a string to store the user's input in.
In C++, the string "4" DOES NOT equal the integer 4.
How will you handle the fact that the variable 'score' is of type
string, but you want to work with integers?
Is there an easy way to get rid of this issue? (hint: use cin)
*/
cout << "This program will calculate the average of four diffrent exam scores." << endl;
/* In the below for-loop, think about what the value of 'score' will be
after each iteration. (Hint, getline replaces the existing value of score).
Also, where is the code that saves the user's inputted numbers to an array?
Learn how to write values to an array http://www.cplusplus.com/doc/tutorial/arrays/
*/
for (int i = 0; i < size; i++) {
studentScores[i] = 0;
cout << "Please Enter Exam Score " << i + 1 << ": ";
getline(cin, score);
}
/* In the for-loop below, you already noticed that your array has random
values in it. The comment about the above for-loop should address that issue.
Equally important though is understanding what the heck is happening in this
loop below. After you fix the bug in the for-loop above (which will
get the values in the array to equal the user's inputs), you'll be faced
with issues in this loop below.
My advice is to understand what happens when the program
executes "studentScores[i]++". First, it gets the number in the array at
the ith+1 position, then it increments that number by 1. Cool, but what
are you doing with the result of that? It's not being put to use anywhere.
Also, note that because the array is never being updated,
the line above it just calculates the same number and stores it in 'result'
every iteration of the loop.
*/
for (int i = 0; i < size; i++) {
result = (studentScores[1] + studentScores[2] + studentScores[3] + studentScores[4]) / size;
studentScores[i]++;
}
cout << "The Average Exam score is " << result << endl;
} while (runAgain());
system("pause");
return 0;
}
// this function doesn't seem to be used, but understanding what
// is wrong with it will help you understand how to code.
// First, note that once a function hits 'return', it will
// never execute any more code in that function.
// So here, your return statement in the for-loop will prevent an
// actual loop from occuring (since the function will exit as soon as the first loop iteration is entered)
// Second, note that you are just getting the value in the array and adding 1 to it
// before dividing it by 'size', which is not the formula for an average.
double average(int studentScores[], int size) {
for (int i = 0; i < size; i++) {
return (studentScores[i]++ / size);
}
}
bool runAgain(void) {
char userResponse;
cout << "\nWould you like to run again (y or n): ";
cin >> userResponse;
if (userResponse == 'y' || userResponse == 'Y')
return(true);
return(false);
}
I hope these comments helped :) Keep at it!
Don't forget that arrays start at index 0. Trying to access studentScores[4]
will give you an unexpected number.
I have been having some issues with implementing a vector. The program that I'm attempting to make does as follows: takes a user defined "size" value and creates a "circular" vector of that size. Then it takes in another integer variable that represents how many iterations of the loop must happen before an item gets deleted from the vector. In my code, I am attempting to iterate through this vector of "Person" objects (a basic class whose only private data member is an integer representing a position). The class that this "runProgram" function is declared in has a private data member of a vector of person objects, then three integers representing the circle size, the number of passes before elimination, and the amount of items in the vector. I have made a while loop to carry-out this procedure. However, every time I run this program, the program seems to make only one pass through the loop, and then delete everything... or just not continue. Here is some of my code:
The main loop:
enter cod int VectorMyJosephus::runProgram(){
int n = 0;
int m = 0;
cout << "How many people would you like in the circle?" << endl;
cin >> n;
cout << "How many passes would you like ther to be before someone is eliminated?" << endl;
cin >> m;
init(n, m);
vector<Person>::iterator iter;
iter = circ.begin();
int count = 0;
while (circ.size() > 1)
{
//count the current person
count += 1;
iter->print();
//remove every "M" person
if (count == M)
{
iter = circ.erase(iter);
count = 0;
size -= 1;
/*printAll();
system("PAUSE");*/
}
else
{
//contine through the list once someone was removed
++iter;
}
if (iter == circ.end())
{
iter = circ.begin();
}
cout << "The position of the only remaining person is: " << iter->getPosition() << "\n" << endl;
cout << circ.size() << endl;
return 0;
}
}e here
Here is my initialization function that is supposed to populate the vector with person objects:
void VectorMyJosephus::init(int N, int M)
{
this->setN(N);
this->setM(M);
this->setSize(N);
for (int i = 0; i < N; i++)
{
Person pers;
pers.setPosition(i);
circ.push_back(pers);
}
}
I'm a beginner, so any help whatsoever is greatly appreciated
You have return 0 inside your while loop, which breaks loop execution immediately.
The assignment requires three functions. The user enters digits 0-9 until they enter a 10, which stops input, and counts each number, then outputs how many of each number has been counted. It should only output if the user entered a number for it.
My only problem is that for every element in the array that the user doesn't use, Xcode counts it as a 0, so the final output has an abnormally large amount of zeros. Everything else works fine.
here is my code
#include <iostream>
using namespace std;
// counter function prototype
void count(int[], int, int []);
// print function prototype
void print(int []);
int main()
{
// define variables and initialize arrays
const int SIZE=100;
int numbers[SIZE], counter[10], input;
// for loop to set all counter elements to 0
for (int assign = 0; assign < 10; assign++)
{
counter[assign]=0;
}
// for loop to collect data
for (int index=0 ; input != 10 ; index++)
{
cout << "Enter a number 0-9, or 10 to terminate: ";
cin >> input;
// while loop to ensure input is 0-10
while (input < 0 || input > 10)
{
cout << "Invalid, please enter 0-9 or 10 to terminate: ";
cin >> input;
}
// if statements to sort input
if (input >= 0 && input <=9)
{
numbers[index] = input;
}
}
// call count function
count(numbers, SIZE, counter);
// call print function
print(counter);
return 0;
}
// counter function
void count(int numbers[], int SIZE, int counter[])
{
// for loop of counter
for (int index = 0 ; index < 10 ; index++)
{
// for loop of numbers
for (int tracker=0 ; tracker < SIZE ; tracker++)
{
// if statement to count each number
if (index == numbers[tracker])
{
counter[index]++;
}
}
}
return;
}
// print function
void print(int counter[])
{
// for loop to print each element
for (int index=0 ; index < 10 ; index++)
{
// if statement to only print numbers that were entered
if (counter[index] > 0)
{
cout << "You entered " << counter[index] << ", " << index << "(s)" << endl;
}
}
return;
}
What you're referring to as "XCode count[ing] as a 0" is actually just the uninitialized value. Given that you've decided to restrict the user's input to 0-9, an easy way of solving this dilemma would be, immediately after you size the array, to iterate through the array and set each value to -1.
Thereafter, when the user finishes their input, instead of just couting every single value, only print it with a conditional like the following:
if (counter[index] != -1)
{
cout << "You entered " << counter[index] << ", " << index << "(s)" << endl;
}
Note that this is the kind of use case that's much better suited to something like a linked list or a vector. As it stands, you're not doing anything to resize the array, or guard against overflow, so if the user attempts to enter more than 100 numbers, you'll run into serious problems.
First off, this isn't an answer to your exact question, but rather a suggestion on how to write your code in a much simpler form.
I'm not going to write this for you, as it's an assignment, and a rather simple one. Looks like you have a good handle on things as far as coding goes.
Consider this:
You need to allow the user to enter 0-10, and count all 0-9's. An array has indices, and a integer of array 10, would hold those 10 numbers you're counting by the indices. Now you just have some empty ints sitting around, so why not use them to count?
A code hint:
++numbers[input];
Second hint: Don't forget to initialize everything to zero.